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