1 /* w5100_socket.c: Wiznet W5100 emulation - sockets code
2 
3    Emulates a minimal subset of the Wiznet W5100 TCP/IP controller.
4 
5    Copyright (c) 2011-2015 Philip Kendall
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation, Inc.,
19    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21    Author contact information:
22 
23    E-mail: philip-fuse@shadowmagic.org.uk
24 
25 */
26 
27 #include <config.h>
28 
29 #include <pthread.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 
34 #ifdef WIN32
35 #include <winsock2.h>
36 #include <ws2tcpip.h>
37 #else
38 #include <arpa/inet.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
41 #endif
42 
43 #include "fuse.h"
44 #include "ui/ui.h"
45 #include "w5100.h"
46 #include "w5100_internals.h"
47 
48 enum w5100_socket_command {
49   W5100_SOCKET_COMMAND_OPEN = 1 << 0,
50   W5100_SOCKET_COMMAND_LISTEN = 1 << 1,
51   W5100_SOCKET_COMMAND_CONNECT = 1 << 2,
52   W5100_SOCKET_COMMAND_DISCON = 1 << 3,
53   W5100_SOCKET_COMMAND_CLOSE = 1 << 4,
54   W5100_SOCKET_COMMAND_SEND = 1 << 5,
55   W5100_SOCKET_COMMAND_RECV = 1 << 6,
56 };
57 
58 static void
w5100_socket_init_common(nic_w5100_socket_t * socket)59 w5100_socket_init_common( nic_w5100_socket_t *socket )
60 {
61   socket->fd = compat_socket_invalid;
62   socket->bind_count = 0;
63   socket->socket_bound = 0;
64   socket->ok_for_io = 0;
65   socket->write_pending = 0;
66 }
67 
68 void
nic_w5100_socket_init(nic_w5100_socket_t * socket,int which)69 nic_w5100_socket_init( nic_w5100_socket_t *socket, int which )
70 {
71   socket->id = which;
72   w5100_socket_init_common( socket );
73   pthread_mutex_init( &socket->lock, NULL );
74 }
75 
76 void
nic_w5100_socket_end(nic_w5100_socket_t * socket)77 nic_w5100_socket_end( nic_w5100_socket_t *socket )
78 {
79   nic_w5100_socket_reset( socket );
80   pthread_mutex_destroy( &socket->lock );
81 }
82 
83 static void
w5100_socket_acquire_lock(nic_w5100_socket_t * socket)84 w5100_socket_acquire_lock( nic_w5100_socket_t *socket )
85 {
86   int error = pthread_mutex_lock( &socket->lock );
87   if( error ) {
88     nic_w5100_error( UI_ERROR_ERROR,
89                      "%s:%d: error %d locking mutex for socket %d\n",
90                      __FILE__, __LINE__, error, socket->id );
91     fuse_abort();
92   }
93 }
94 
95 static void
w5100_socket_release_lock(nic_w5100_socket_t * socket)96 w5100_socket_release_lock( nic_w5100_socket_t *socket )
97 {
98   int error = pthread_mutex_unlock( &socket->lock );
99   if( error ) {
100     nic_w5100_debug( "%s:%d: error %d unlocking mutex for socket %d\n", __FILE__, __LINE__, error, socket->id );
101     fuse_abort();
102   }
103 }
104 
105 static void
w5100_socket_clean(nic_w5100_socket_t * socket)106 w5100_socket_clean( nic_w5100_socket_t *socket )
107 {
108   socket->ir = 0;
109   memset( socket->port, 0, sizeof( socket->port ) );
110   memset( socket->dip, 0, sizeof( socket->dip ) );
111   memset( socket->dport, 0, sizeof( socket->dport ) );
112   socket->tx_rr = socket->tx_wr = 0;
113   socket->rx_rsr = 0;
114   socket->old_rx_rd = socket->rx_rd = 0;
115 
116   socket->last_send = 0;
117   socket->datagram_count = 0;
118 
119   if( socket->fd != compat_socket_invalid ) {
120     compat_socket_close( socket->fd );
121     w5100_socket_init_common( socket );
122   }
123 }
124 
125 void
nic_w5100_socket_reset(nic_w5100_socket_t * socket)126 nic_w5100_socket_reset( nic_w5100_socket_t *socket )
127 {
128   w5100_socket_acquire_lock( socket );
129 
130   socket->mode = W5100_SOCKET_MODE_CLOSED;
131   socket->flags = 0;
132   socket->state = W5100_SOCKET_STATE_CLOSED;
133 
134   w5100_socket_clean( socket );
135 
136   w5100_socket_release_lock( socket );
137 }
138 
139 static void
w5100_write_socket_mr(nic_w5100_socket_t * socket,libspectrum_byte b)140 w5100_write_socket_mr( nic_w5100_socket_t *socket, libspectrum_byte b )
141 {
142   w5100_socket_mode mode = b & 0x0f;
143   libspectrum_byte flags = b & 0xf0;
144 
145   nic_w5100_debug( "w5100: writing 0x%02x to S%d_MR\n", b, socket->id );
146 
147   switch( mode ) {
148     case W5100_SOCKET_MODE_CLOSED:
149       break;
150     case W5100_SOCKET_MODE_TCP:
151       /* We support only "disable no delayed ACK" */
152       if( flags != 0x20 ) {
153         ui_error( UI_ERROR_WARNING, "w5100: unsupported flags 0x%02x set for TCP mode on socket %d\n", b & 0xf0, socket->id );
154         flags = 0x20;
155       }
156       break;
157     case W5100_SOCKET_MODE_UDP:
158       /* We don't support multicast */
159       if( flags != 0x00 ) {
160         ui_error( UI_ERROR_WARNING, "w5100: unsupported flags 0x%02x set for UDP mode on socket %d\n", b & 0xf0, socket->id );
161         flags = 0;
162       }
163       break;
164     case W5100_SOCKET_MODE_IPRAW:
165     case W5100_SOCKET_MODE_MACRAW:
166     case W5100_SOCKET_MODE_PPPOE:
167     default:
168       ui_error( UI_ERROR_WARNING, "w5100: unsupported mode 0x%02x set on socket %d\n", b, socket->id );
169       mode = W5100_SOCKET_MODE_CLOSED;
170       break;
171   }
172 
173   socket->mode = mode;
174   socket->flags = flags;
175 }
176 
177 static void
w5100_socket_open(nic_w5100_socket_t * socket_obj)178 w5100_socket_open( nic_w5100_socket_t *socket_obj )
179 {
180   if( ( socket_obj->mode == W5100_SOCKET_MODE_UDP ||
181       socket_obj->mode == W5100_SOCKET_MODE_TCP ) &&
182     socket_obj->state == W5100_SOCKET_STATE_CLOSED ) {
183 
184     int tcp = socket_obj->mode == W5100_SOCKET_MODE_TCP;
185     int type = tcp ? SOCK_STREAM : SOCK_DGRAM;
186     int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP;
187     const char *description = tcp ? "TCP" : "UDP";
188     int final_state = tcp ? W5100_SOCKET_STATE_INIT : W5100_SOCKET_STATE_UDP;
189 #ifndef WIN32
190     int one = 1;
191 #endif
192 
193     w5100_socket_clean( socket_obj );
194 
195     socket_obj->fd = socket( AF_INET, type, protocol );
196     if( socket_obj->fd == compat_socket_invalid ) {
197       nic_w5100_error( UI_ERROR_ERROR,
198         "w5100: failed to open %s socket for socket %d; errno %d: %s\n",
199         description, socket_obj->id, compat_socket_get_error(),
200         compat_socket_get_strerror() );
201       return;
202     }
203 
204 #ifndef WIN32
205     /* Windows warning: this could forcibly bind sockets already in use */
206     if( setsockopt( socket_obj->fd, SOL_SOCKET, SO_REUSEADDR, &one,
207       sizeof(one) ) == -1 ) {
208       nic_w5100_error( UI_ERROR_ERROR,
209         "w5100: failed to set SO_REUSEADDR on socket %d; errno %d: %s\n",
210         socket_obj->id, compat_socket_get_error(),
211         compat_socket_get_strerror() );
212     }
213 #endif
214 
215     socket_obj->state = final_state;
216 
217     nic_w5100_debug( "w5100: opened %s fd %d for socket %d\n", description, socket_obj->fd, socket_obj->id );
218   }
219 }
220 
221 static int
w5100_socket_bind_port(nic_w5100_t * self,nic_w5100_socket_t * socket)222 w5100_socket_bind_port( nic_w5100_t *self, nic_w5100_socket_t *socket )
223 {
224   struct sockaddr_in sa;
225 
226   memset( &sa, 0, sizeof(sa) );
227   sa.sin_family = AF_INET;
228   memcpy( &sa.sin_port, socket->port, 2 );
229   memcpy( &sa.sin_addr.s_addr, self->sip, 4 );
230 
231   nic_w5100_debug( "w5100: attempting to bind socket %d to %s:%d\n", socket->id, inet_ntoa(sa.sin_addr), ntohs(sa.sin_port) );
232   if( bind( socket->fd, (struct sockaddr*)&sa, sizeof(sa) ) == -1 ) {
233     nic_w5100_error( UI_ERROR_ERROR,
234                      "w5100: failed to bind socket %d; errno %d: %s\n",
235                      socket->id, compat_socket_get_error(),
236                      compat_socket_get_strerror() );
237 
238     socket->ir |= 1 << 3;
239     socket->state = W5100_SOCKET_STATE_CLOSED;
240     return -1;
241   }
242 
243   socket->socket_bound = 1;
244   nic_w5100_debug( "w5100: successfully bound socket %d\n", socket->id );
245 
246   return 0;
247 }
248 
249 static void
w5100_socket_listen(nic_w5100_t * self,nic_w5100_socket_t * socket)250 w5100_socket_listen( nic_w5100_t *self, nic_w5100_socket_t *socket )
251 {
252   if( socket->state == W5100_SOCKET_STATE_INIT ) {
253 
254     if( !socket->socket_bound )
255       if( w5100_socket_bind_port( self, socket ) )
256         return;
257 
258     if( listen( socket->fd, 1 ) == -1 ) {
259       nic_w5100_error( UI_ERROR_ERROR,
260                        "w5100: failed to listen on socket %d; errno %d: %s\n",
261                        socket->id, compat_socket_get_error(),
262                        compat_socket_get_strerror() );
263       return;
264     }
265 
266     socket->state = W5100_SOCKET_STATE_LISTEN;
267 
268     nic_w5100_debug( "w5100: listening on socket %d\n", socket->id );
269 
270     compat_socket_selfpipe_wake( self->selfpipe );
271   }
272 }
273 
274 static void
w5100_socket_connect(nic_w5100_t * self,nic_w5100_socket_t * socket)275 w5100_socket_connect( nic_w5100_t *self, nic_w5100_socket_t *socket )
276 {
277   if( socket->state == W5100_SOCKET_STATE_INIT ) {
278     struct sockaddr_in sa;
279 
280     if( !socket->socket_bound )
281       if( w5100_socket_bind_port( self, socket ) )
282         return;
283 
284     memset( &sa, 0, sizeof(sa) );
285     sa.sin_family = AF_INET;
286     memcpy( &sa.sin_port, socket->dport, 2 );
287     memcpy( &sa.sin_addr.s_addr, socket->dip, 4 );
288 
289     if( connect( socket->fd, (struct sockaddr*)&sa, sizeof(sa) ) == -1 ) {
290       nic_w5100_error( UI_ERROR_ERROR,
291         "w5100: failed to connect socket %d to 0x%08x:0x%04x; errno %d: %s\n",
292         socket->id, ntohl(sa.sin_addr.s_addr), ntohs(sa.sin_port),
293         compat_socket_get_error(), compat_socket_get_strerror() );
294 
295       socket->ir |= 1 << 3;
296       socket->state = W5100_SOCKET_STATE_CLOSED;
297       return;
298     }
299 
300     socket->ir |= 1 << 0;
301     socket->state = W5100_SOCKET_STATE_ESTABLISHED;
302   }
303 }
304 
305 static void
w5100_socket_discon(nic_w5100_t * self,nic_w5100_socket_t * socket)306 w5100_socket_discon( nic_w5100_t *self, nic_w5100_socket_t *socket )
307 {
308   if( socket->state == W5100_SOCKET_STATE_ESTABLISHED ||
309     socket->state == W5100_SOCKET_STATE_CLOSE_WAIT ) {
310     socket->ir |= 1 << 1;
311     socket->state = W5100_SOCKET_STATE_CLOSED;
312     compat_socket_selfpipe_wake( self->selfpipe );
313 
314     nic_w5100_debug( "w5100: disconnected socket %d\n", socket->id );
315   }
316 }
317 
318 static void
w5100_socket_close(nic_w5100_t * self,nic_w5100_socket_t * socket)319 w5100_socket_close( nic_w5100_t *self, nic_w5100_socket_t *socket )
320 {
321   if( socket->fd != compat_socket_invalid ) {
322     compat_socket_close( socket->fd );
323     socket->fd = compat_socket_invalid;
324     socket->socket_bound = 0;
325     socket->ok_for_io = 0;
326     socket->state = W5100_SOCKET_STATE_CLOSED;
327     compat_socket_selfpipe_wake( self->selfpipe );
328     nic_w5100_debug( "w5100: closed socket %d\n", socket->id );
329   }
330 }
331 
332 static void
w5100_socket_send(nic_w5100_t * self,nic_w5100_socket_t * socket)333 w5100_socket_send( nic_w5100_t *self, nic_w5100_socket_t *socket )
334 {
335   if( socket->state == W5100_SOCKET_STATE_UDP ) {
336 
337     if( !socket->socket_bound )
338       if( w5100_socket_bind_port( self, socket ) )
339         return;
340 
341     socket->datagram_lengths[socket->datagram_count++] =
342       socket->tx_wr - socket->last_send;
343     socket->last_send = socket->tx_wr;
344     socket->write_pending = 1;
345     compat_socket_selfpipe_wake( self->selfpipe );
346   }
347   else if( socket->state == W5100_SOCKET_STATE_ESTABLISHED ) {
348     socket->write_pending = 1;
349     compat_socket_selfpipe_wake( self->selfpipe );
350   }
351 }
352 
353 static void
w5100_socket_recv(nic_w5100_t * self,nic_w5100_socket_t * socket)354 w5100_socket_recv( nic_w5100_t *self, nic_w5100_socket_t *socket )
355 {
356   if( socket->state == W5100_SOCKET_STATE_UDP ||
357     socket->state == W5100_SOCKET_STATE_ESTABLISHED ) {
358     socket->rx_rsr -= socket->rx_rd - socket->old_rx_rd;
359     socket->old_rx_rd = socket->rx_rd;
360     if( socket->rx_rsr != 0 )
361       socket->ir |= 1 << 2;
362     compat_socket_selfpipe_wake( self->selfpipe );
363   }
364 }
365 
366 static void
w5100_write_socket_cr(nic_w5100_t * self,nic_w5100_socket_t * socket,libspectrum_byte b)367 w5100_write_socket_cr( nic_w5100_t *self, nic_w5100_socket_t *socket, libspectrum_byte b )
368 {
369   nic_w5100_debug( "w5100: writing 0x%02x to S%d_CR\n", b, socket->id );
370 
371   switch( b ) {
372     case W5100_SOCKET_COMMAND_OPEN:
373       w5100_socket_open( socket );
374       break;
375     case W5100_SOCKET_COMMAND_LISTEN:
376       w5100_socket_listen( self, socket );
377       break;
378     case W5100_SOCKET_COMMAND_CONNECT:
379       w5100_socket_connect( self, socket );
380       break;
381     case W5100_SOCKET_COMMAND_DISCON:
382       w5100_socket_discon( self, socket );
383       break;
384     case W5100_SOCKET_COMMAND_CLOSE:
385       w5100_socket_close( self, socket );
386       break;
387     case W5100_SOCKET_COMMAND_SEND:
388       w5100_socket_send( self, socket );
389       break;
390     case W5100_SOCKET_COMMAND_RECV:
391       w5100_socket_recv( self, socket );
392       break;
393     default:
394       ui_error( UI_ERROR_WARNING, "w5100: unknown command 0x%02x sent to socket %d\n", b, socket->id );
395       break;
396   }
397 }
398 
399 static void
w5100_write_socket_port(nic_w5100_t * self,nic_w5100_socket_t * socket,int which,libspectrum_byte b)400 w5100_write_socket_port( nic_w5100_t *self, nic_w5100_socket_t *socket, int which, libspectrum_byte b )
401 {
402   nic_w5100_debug( "w5100: writing 0x%02x to S%d_PORT%d\n", b, socket->id, which );
403   socket->port[which] = b;
404   if( ++socket->bind_count == 2 ) {
405     if( socket->state == W5100_SOCKET_STATE_UDP && !socket->socket_bound ) {
406       if( w5100_socket_bind_port( self, socket ) ) {
407         socket->bind_count = 0;
408         return;
409       }
410       compat_socket_selfpipe_wake( self->selfpipe );
411     }
412     socket->bind_count = 0;
413   }
414 }
415 
416 libspectrum_byte
nic_w5100_socket_read(nic_w5100_t * self,libspectrum_word reg)417 nic_w5100_socket_read( nic_w5100_t *self, libspectrum_word reg )
418 {
419   nic_w5100_socket_t *socket = &self->socket[(reg >> 8) - 4];
420   int socket_reg = reg & 0xff;
421   int reg_offset;
422   libspectrum_word fsr;
423   libspectrum_byte b;
424 
425   w5100_socket_acquire_lock( socket );
426 
427   switch( socket_reg ) {
428     case W5100_SOCKET_MR:
429       b = socket->mode;
430       nic_w5100_debug( "w5100: reading 0x%02x from S%d_MR\n", b, socket->id );
431       break;
432     case W5100_SOCKET_IR:
433       b = socket->ir;
434       nic_w5100_debug( "w5100: reading 0x%02x from S%d_IR\n", b, socket->id );
435       break;
436     case W5100_SOCKET_SR:
437       b = socket->state;
438       nic_w5100_debug( "w5100: reading 0x%02x from S%d_SR\n", b, socket->id );
439       break;
440     case W5100_SOCKET_PORT0: case W5100_SOCKET_PORT1:
441       b = socket->port[socket_reg - W5100_SOCKET_PORT0];
442       nic_w5100_debug( "w5100: reading 0x%02x from S%d_PORT%d\n", b, socket->id, socket_reg - W5100_SOCKET_PORT0 );
443       break;
444     case W5100_SOCKET_TX_FSR0: case W5100_SOCKET_TX_FSR1:
445       reg_offset = socket_reg - W5100_SOCKET_TX_FSR0;
446       fsr = 0x0800 - (socket->tx_wr - socket->tx_rr);
447       b = ( fsr >> ( 8 * ( 1 - reg_offset ) ) ) & 0xff;
448       nic_w5100_debug( "w5100: reading 0x%02x from S%d_TX_FSR%d\n", b, socket->id, reg_offset );
449       break;
450     case W5100_SOCKET_TX_RR0: case W5100_SOCKET_TX_RR1:
451       reg_offset = socket_reg - W5100_SOCKET_TX_RR0;
452       b = ( socket->tx_rr >> ( 8 * ( 1 - reg_offset ) ) ) & 0xff;
453       nic_w5100_debug( "w5100: reading 0x%02x from S%d_TX_RR%d\n", b, socket->id, reg_offset );
454       break;
455     case W5100_SOCKET_TX_WR0: case W5100_SOCKET_TX_WR1:
456       reg_offset = socket_reg - W5100_SOCKET_TX_WR0;
457       b = ( socket->tx_wr >> ( 8 * ( 1 - reg_offset ) ) ) & 0xff;
458       nic_w5100_debug( "w5100: reading 0x%02x from S%d_TX_WR%d\n", b, socket->id, reg_offset );
459       break;
460     case W5100_SOCKET_RX_RSR0: case W5100_SOCKET_RX_RSR1:
461       reg_offset = socket_reg - W5100_SOCKET_RX_RSR0;
462       b = ( socket->rx_rsr >> ( 8 * ( 1 - reg_offset ) ) ) & 0xff;
463       nic_w5100_debug( "w5100: reading 0x%02x from S%d_RX_RSR%d\n", b, socket->id, reg_offset );
464       break;
465     case W5100_SOCKET_RX_RD0: case W5100_SOCKET_RX_RD1:
466       reg_offset = socket_reg - W5100_SOCKET_RX_RD0;
467       b = ( socket->rx_rd >> ( 8 * ( 1 - reg_offset ) ) ) & 0xff;
468       nic_w5100_debug( "w5100: reading 0x%02x from S%d_RX_RD%d\n", b, socket->id, reg_offset );
469       break;
470     default:
471       b = 0xff;
472       ui_error( UI_ERROR_WARNING, "w5100: reading 0x%02x from unsupported register 0x%03x\n", b, reg );
473       break;
474   }
475 
476   w5100_socket_release_lock( socket );
477 
478   return b;
479 }
480 
481 void
nic_w5100_socket_write(nic_w5100_t * self,libspectrum_word reg,libspectrum_byte b)482 nic_w5100_socket_write( nic_w5100_t *self, libspectrum_word reg, libspectrum_byte b )
483 {
484   nic_w5100_socket_t *socket = &self->socket[(reg >> 8) - 4];
485   int socket_reg = reg & 0xff;
486 
487   w5100_socket_acquire_lock( socket );
488 
489   switch( socket_reg ) {
490     case W5100_SOCKET_MR:
491       w5100_write_socket_mr( socket, b );
492       break;
493     case W5100_SOCKET_CR:
494       w5100_write_socket_cr( self, socket, b );
495       break;
496     case W5100_SOCKET_IR:
497       nic_w5100_debug( "w5100: writing 0x%02x to S%d_IR\n", b, socket->id );
498       socket->ir &= ~b;
499       break;
500     case W5100_SOCKET_PORT0: case W5100_SOCKET_PORT1:
501       w5100_write_socket_port( self, socket, socket_reg - W5100_SOCKET_PORT0, b );
502       break;
503     case W5100_SOCKET_DIPR0: case W5100_SOCKET_DIPR1: case W5100_SOCKET_DIPR2: case W5100_SOCKET_DIPR3:
504       nic_w5100_debug( "w5100: writing 0x%02x to S%d_DIPR%d\n", b, socket->id, socket_reg - W5100_SOCKET_DIPR0 );
505       socket->dip[socket_reg - W5100_SOCKET_DIPR0] = b;
506       break;
507     case W5100_SOCKET_DPORT0: case W5100_SOCKET_DPORT1:
508       nic_w5100_debug( "w5100: writing 0x%02x to S%d_DPORT%d\n", b, socket->id, socket_reg - W5100_SOCKET_DPORT0 );
509       socket->dport[socket_reg - W5100_SOCKET_DPORT0] = b;
510       break;
511     case W5100_SOCKET_TX_WR0:
512       nic_w5100_debug( "w5100: writing 0x%02x to S%d_TX_WR0\n", b, socket->id );
513       socket->tx_wr = (socket->tx_wr & 0xff) | (b << 8);
514       break;
515     case W5100_SOCKET_TX_WR1:
516       nic_w5100_debug( "w5100: writing 0x%02x to S%d_TX_WR1\n", b, socket->id );
517       socket->tx_wr = (socket->tx_wr & 0xff00) | b;
518       break;
519     case W5100_SOCKET_RX_RD0:
520       nic_w5100_debug( "w5100: writing 0x%02x to S%d_RX_RD0\n", b, socket->id );
521       socket->rx_rd = (socket->rx_rd & 0xff) | (b << 8);
522       break;
523     case W5100_SOCKET_RX_RD1:
524       nic_w5100_debug( "w5100: writing 0x%02x to S%d_RX_RD1\n", b, socket->id );
525       socket->rx_rd = (socket->rx_rd & 0xff00) | b;
526       break;
527     default:
528       ui_error( UI_ERROR_WARNING, "w5100: writing 0x%02x to unsupported register 0x%03x\n", b, reg );
529       break;
530   }
531 
532   if( socket_reg != W5100_SOCKET_PORT0 && socket_reg != W5100_SOCKET_PORT1 )
533     socket->bind_count = 0;
534 
535   w5100_socket_release_lock( socket );
536 }
537 
538 libspectrum_byte
nic_w5100_socket_read_rx_buffer(nic_w5100_t * self,libspectrum_word reg)539 nic_w5100_socket_read_rx_buffer( nic_w5100_t *self, libspectrum_word reg )
540 {
541   nic_w5100_socket_t *socket = &self->socket[(reg - 0x6000) / 0x0800];
542   int offset = reg & 0x7ff;
543   libspectrum_byte b = socket->rx_buffer[offset];
544   nic_w5100_debug( "w5100: reading 0x%02x from socket %d rx buffer offset 0x%03x\n", b, socket->id, offset );
545   return b;
546 }
547 
548 void
nic_w5100_socket_write_tx_buffer(nic_w5100_t * self,libspectrum_word reg,libspectrum_byte b)549 nic_w5100_socket_write_tx_buffer( nic_w5100_t *self, libspectrum_word reg, libspectrum_byte b )
550 {
551   nic_w5100_socket_t *socket = &self->socket[(reg - 0x4000) / 0x0800];
552   int offset = reg & 0x7ff;
553   nic_w5100_debug( "w5100: writing 0x%02x to socket %d tx buffer offset 0x%03x\n", b, socket->id, offset );
554   socket->tx_buffer[offset] = b;
555 }
556 
557 void
nic_w5100_socket_add_to_sets(nic_w5100_socket_t * socket,fd_set * readfds,fd_set * writefds,int * max_fd)558 nic_w5100_socket_add_to_sets( nic_w5100_socket_t *socket, fd_set *readfds,
559   fd_set *writefds, int *max_fd )
560 {
561   w5100_socket_acquire_lock( socket );
562 
563   if( socket->fd != compat_socket_invalid ) {
564     /* We can process a UDP read if we're in a UDP state and there are at least
565        9 bytes free in our buffer (8 byte UDP header and 1 byte of actual
566        data). */
567     int udp_read = socket->state == W5100_SOCKET_STATE_UDP &&
568       0x800 - socket->rx_rsr >= 9;
569     /* We can process a TCP read if we're in the established state and have
570        any room in our buffer (no header necessary for TCP). */
571     int tcp_read = socket->state == W5100_SOCKET_STATE_ESTABLISHED &&
572       0x800 - socket->rx_rsr >= 1;
573 
574     int tcp_listen = socket->state == W5100_SOCKET_STATE_LISTEN;
575 
576     socket->ok_for_io = 1;
577 
578     if( udp_read || tcp_read || tcp_listen ) {
579       FD_SET( socket->fd, readfds );
580       if( socket->fd > *max_fd )
581         *max_fd = socket->fd;
582       nic_w5100_debug( "w5100: checking for read on socket %d with fd %d; max fd %d\n", socket->id, socket->fd, *max_fd );
583     }
584 
585     if( socket->write_pending ) {
586       FD_SET( socket->fd, writefds );
587       if( socket->fd > *max_fd )
588         *max_fd = socket->fd;
589       nic_w5100_debug( "w5100: write pending on socket %d with fd %d; max fd %d\n", socket->id, socket->fd, *max_fd );
590     }
591   }
592 
593   w5100_socket_release_lock( socket );
594 }
595 
596 static void
w5100_socket_process_accept(nic_w5100_socket_t * socket)597 w5100_socket_process_accept( nic_w5100_socket_t *socket )
598 {
599   struct sockaddr_in sa;
600   socklen_t sa_length = sizeof(sa);
601   compat_socket_t new_fd;
602 
603   memset( &sa, 0, sizeof(sa) );
604 
605   new_fd = accept( socket->fd, (struct sockaddr*)&sa, &sa_length );
606   if( new_fd == compat_socket_invalid ) {
607     nic_w5100_debug( "w5100: error from accept on socket %d; errno %d: %s\n",
608                      socket->id, compat_socket_get_error(),
609                      compat_socket_get_strerror() );
610     return;
611   }
612 
613   nic_w5100_debug( "w5100: accepted connection from %s:%d on socket %d\n", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port), socket->id );
614 
615   if( compat_socket_close( socket->fd ) == -1 )
616     nic_w5100_debug( "w5100: error attempting to close fd %d for socket %d\n", socket->fd, socket->id );
617 
618   socket->fd = new_fd;
619   socket->state = W5100_SOCKET_STATE_ESTABLISHED;
620 }
621 
622 static void
w5100_socket_process_read(nic_w5100_socket_t * socket)623 w5100_socket_process_read( nic_w5100_socket_t *socket )
624 {
625   libspectrum_byte buffer[0x800];
626   int bytes_free = 0x800 - socket->rx_rsr;
627   ssize_t bytes_read;
628   struct sockaddr_in sa;
629 
630   int udp = socket->state == W5100_SOCKET_STATE_UDP;
631   const char *description = udp ? "UDP" : "TCP";
632 
633   nic_w5100_debug( "w5100: reading from socket %d\n", socket->id );
634 
635   if( udp ) {
636     socklen_t sa_length = sizeof(sa);
637     bytes_read = recvfrom( socket->fd, (char*)buffer + 8, bytes_free - 8, 0,
638       (struct sockaddr*)&sa, &sa_length );
639   }
640   else
641     bytes_read = recv( socket->fd, (char*)buffer, bytes_free, 0 );
642 
643   nic_w5100_debug( "w5100: read 0x%03x bytes from %s socket %d\n", (int)bytes_read, description, socket->id );
644 
645   if( bytes_read > 0 || (udp && bytes_read == 0) ) {
646     int offset = (socket->old_rx_rd + socket->rx_rsr) & 0x7ff;
647     libspectrum_byte *dest = &socket->rx_buffer[offset];
648 
649     if( udp ) {
650       /* Add the W5100's UDP header */
651       memcpy( buffer, &sa.sin_addr.s_addr, 4 );
652       memcpy( buffer + 4, &sa.sin_port, 2 );
653       buffer[6] = (bytes_read >> 8) & 0xff;
654       buffer[7] = bytes_read & 0xff;
655       bytes_read += 8;
656     }
657 
658     socket->rx_rsr += bytes_read;
659     socket->ir |= 1 << 2;
660 
661     if( offset + bytes_read <= 0x800 ) {
662       memcpy( dest, buffer, bytes_read );
663     }
664     else {
665       int first_chunk = 0x800 - offset;
666       memcpy( dest, buffer, first_chunk );
667       memcpy( socket->rx_buffer, buffer + first_chunk, bytes_read - first_chunk );
668     }
669   }
670   else if( bytes_read == 0 ) {  /* TCP */
671     socket->state = W5100_SOCKET_STATE_CLOSE_WAIT;
672     nic_w5100_debug( "w5100: EOF on %s socket %d; errno %d: %s\n",
673                      description, socket->id, compat_socket_get_error(),
674                      compat_socket_get_strerror() );
675   }
676   else {
677     nic_w5100_debug( "w5100: error %d reading from %s socket %d: %s\n",
678                      compat_socket_get_error(), description, socket->id,
679                      compat_socket_get_strerror() );
680   }
681 }
682 
683 static void
w5100_socket_process_udp_write(nic_w5100_socket_t * socket)684 w5100_socket_process_udp_write( nic_w5100_socket_t *socket )
685 {
686   ssize_t bytes_sent;
687   int offset = socket->tx_rr & 0x7ff;
688   libspectrum_word length = socket->datagram_lengths[0];
689   libspectrum_byte *data = &socket->tx_buffer[ offset ];
690   struct sockaddr_in sa;
691   libspectrum_byte buffer[0x800];
692 
693   nic_w5100_debug( "w5100: writing to UDP socket %d\n", socket->id );
694 
695   /* If the data wraps round the write buffer, we need to coalesce it into
696      one chunk for the call to sendto() */
697   if( offset + length > 0x800 ) {
698     int first_chunk = 0x800 - offset;
699     memcpy( buffer, data, first_chunk );
700     memcpy( buffer + first_chunk, socket->tx_buffer, length - first_chunk );
701     data = buffer;
702   }
703 
704   memset( &sa, 0, sizeof(sa) );
705   sa.sin_family = AF_INET;
706   memcpy( &sa.sin_port, socket->dport, 2 );
707   memcpy( &sa.sin_addr.s_addr, socket->dip, 4 );
708 
709   bytes_sent = sendto( socket->fd, (const char*)data, length, 0, (struct sockaddr*)&sa, sizeof(sa) );
710   nic_w5100_debug( "w5100: sent 0x%03x bytes of 0x%03x to UDP socket %d\n",
711                    (int)bytes_sent, length, socket->id );
712 
713   if( bytes_sent == length ) {
714     if( --socket->datagram_count )
715       memmove( socket->datagram_lengths, &socket->datagram_lengths[1],
716         0x1f * sizeof(int) );
717 
718     socket->tx_rr += bytes_sent;
719     if( socket->datagram_count == 0 ) {
720       socket->write_pending = 0;
721       socket->ir |= 1 << 4;
722     }
723   }
724   else if( bytes_sent != -1 )
725     nic_w5100_debug( "w5100: didn't manage to send full datagram to UDP socket %d?\n", socket->id );
726   else
727     nic_w5100_debug( "w5100: error %d writing to UDP socket %d: %s\n",
728                      compat_socket_get_error(), socket->id,
729                      compat_socket_get_strerror() );
730 }
731 
732 static void
w5100_socket_process_tcp_write(nic_w5100_socket_t * socket)733 w5100_socket_process_tcp_write( nic_w5100_socket_t *socket )
734 {
735   ssize_t bytes_sent;
736   int offset = socket->tx_rr & 0x7ff;
737   libspectrum_word length = socket->tx_wr - socket->tx_rr;
738   libspectrum_byte *data = &socket->tx_buffer[ offset ];
739 
740   nic_w5100_debug( "w5100: writing to TCP socket %d\n", socket->id );
741 
742   /* If the data wraps round the write buffer, write it in two chunks */
743   if( offset + length > 0x800 )
744     length = 0x800 - offset;
745 
746   bytes_sent = send( socket->fd, (const char*)data, length, 0 );
747   nic_w5100_debug( "w5100: sent 0x%03x bytes of 0x%03x to TCP socket %d\n",
748                    (int)bytes_sent, length, socket->id );
749 
750   if( bytes_sent != -1 ) {
751     socket->tx_rr += bytes_sent;
752     if( socket->tx_rr == socket->tx_wr ) {
753       socket->write_pending = 0;
754       socket->ir |= 1 << 4;
755     }
756   }
757   else
758     nic_w5100_debug( "w5100: error %d writing to TCP socket %d: %s\n",
759                      compat_socket_get_error(), socket->id,
760                      compat_socket_get_strerror() );
761 }
762 
763 void
nic_w5100_socket_process_io(nic_w5100_socket_t * socket,fd_set readfds,fd_set writefds)764 nic_w5100_socket_process_io( nic_w5100_socket_t *socket, fd_set readfds,
765   fd_set writefds )
766 {
767   w5100_socket_acquire_lock( socket );
768 
769   /* Process only if we're an open socket, and we haven't been closed and
770      re-opened since the select() started */
771   if( socket->fd != compat_socket_invalid && socket->ok_for_io ) {
772     if( FD_ISSET( socket->fd, &readfds ) ) {
773       if( socket->state == W5100_SOCKET_STATE_LISTEN )
774         w5100_socket_process_accept( socket );
775       else
776         w5100_socket_process_read( socket );
777     }
778 
779     if( FD_ISSET( socket->fd, &writefds ) ) {
780       if( socket->state == W5100_SOCKET_STATE_UDP ) {
781         w5100_socket_process_udp_write( socket );
782       }
783       else if( socket->state == W5100_SOCKET_STATE_ESTABLISHED ) {
784         w5100_socket_process_tcp_write( socket );
785       }
786     }
787   }
788 
789   w5100_socket_release_lock( socket );
790 }
791