1 /*  Copyright 2006, 2013 Theo Berkau
2 
3     This file is part of Yabause.
4 
5     Yabause 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     Yabause 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 Yabause; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 */
19 
20 /*! \file netlink.c
21     \brief Netlink emulation functions.
22 */
23 
24 
25 #include <ctype.h>
26 #include "cs2.h"
27 #include "error.h"
28 #include "netlink.h"
29 #include "debug.h"
30 #include "scu.h"
31 #ifdef USESOCKET
32 #include "sock.h"
33 #include "threads.h"
34 #endif
35 
36 Netlink *NetlinkArea = NULL;
37 
38 static volatile u8 netlink_listener_thread_running;
39 static volatile u8 netlink_connect_thread_running;
40 static volatile u8 netlink_client_thread_running;
41 
42 static int NetworkInit(const char *port);
43 static void NetworkDeInit(void);
44 static void NetworkStopClient();
45 static void NetworkStopConnect();
46 static void NetworkConnect(const char *ip, const char *port);
47 static int NetworkWaitForConnect();
48 static int NetworkSend(const void *buffer, int length);
49 static int NetworkReceive(void *buffer, int maxlength);
50 
51 //////////////////////////////////////////////////////////////////////////////
52 
53 /*!
54  * Sets LSR value after checking if an interrupt needs to be triggered
55  * @param[in]  val new LSR value
56  */
NetlinkLSRChange(u8 val)57 UNUSED static void NetlinkLSRChange(u8 val)
58 {
59    // If IER bit 2 is set and if any of the error or alarms bits are set(and
60    // they weren't previously), trigger an interrupt
61    if ((NetlinkArea->reg.IER & 0x4) && ((NetlinkArea->reg.LSR ^ val) & val & 0x1E))
62    {
63       NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x6;
64       ScuSendExternalInterrupt12();
65    }
66 
67    NetlinkArea->reg.LSR = val;
68 }
69 
70 //////////////////////////////////////////////////////////////////////////////
71 
72 #ifndef USESOCKET
73 UNUSED
74 #endif
75 /*!
76  * Sets MSR value after checking if an interrupt needs to be triggered
77  * @param[in]  set   bitmask to set
78  * @param[in]  clear bitmask to clear
79  */
NetlinkMSRChange(u8 set,u8 clear)80 static void NetlinkMSRChange(u8 set, u8 clear)
81 {
82    u8 change;
83 
84    change = ((NetlinkArea->reg.MSR >> 4) ^ set) & set;
85    change |= (((NetlinkArea->reg.MSR >> 4) ^ 0xFF) ^ clear) & clear;
86 
87    // If IER bit 3 is set and CTS/DSR/RI/RLSD changes, trigger interrupt
88    if ((NetlinkArea->reg.IER & 0x8) && change)
89    {
90       NetlinkArea->reg.IIR = NetlinkArea->reg.IIR & 0xF0;
91       ScuSendExternalInterrupt12();
92    }
93 
94    NetlinkArea->reg.MSR &= ~(clear << 4);
95    NetlinkArea->reg.MSR |= (set << 4) | change;
96 }
97 
98 //////////////////////////////////////////////////////////////////////////////
99 
100 /*!
101  * Reads register(byte) from Netlink address space
102  * @param[in]  sh   SH2 context
103  * @param[in]  addr Address to read
104  * \return data from register
105  */
NetlinkReadByte(SH2_struct * sh,u32 addr)106 u8 FASTCALL NetlinkReadByte(SH2_struct *sh, u32 addr)
107 {
108    u8 ret;
109 
110    switch (addr & 0xFFFFF)
111    {
112       case 0x95001: // Receiver Buffer/Divisor Latch Low Byte
113       {
114          if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch Low Byte
115             return NetlinkArea->reg.DLL;
116          else // Receiver Buffer
117          {
118             if (NetlinkArea->outbuffersize == 0)
119             {
120 #ifdef USESOCKET
121                YabThreadWake(YAB_THREAD_NETLINKCLIENT);
122 #endif
123                return 0x00;
124             }
125 
126             ret = NetlinkArea->outbuffer[NetlinkArea->outbufferstart];
127             NetlinkArea->outbufferstart++;
128             NetlinkArea->outbuffersize--;
129 
130             // If the buffer is empty now, make sure the data available
131             // bit in LSR is cleared
132             if (NetlinkArea->outbuffersize == 0)
133             {
134                NetlinkArea->outbufferstart = NetlinkArea->outbufferend = 0;
135                NetlinkArea->reg.LSR &= ~0x01;
136             }
137 
138             // If interrupt has been triggered because of RBR having data, reset it
139             if ((NetlinkArea->reg.IER & 0x1) && (NetlinkArea->reg.IIR & 0xF) == 0x4)
140                NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1;
141 
142             return ret;
143          }
144 
145          return 0;
146       }
147       case 0x95005: // Interrupt Enable Register/Divisor Latch High Byte
148       {
149          if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch High Byte
150             return NetlinkArea->reg.DLM;
151          else // Interrupt Enable Register
152             return NetlinkArea->reg.IER;
153       }
154       case 0x95009: // Interrupt Identification Register
155       {
156          // If interrupt has been triggered because THB is empty, reset it
157          if ((NetlinkArea->reg.IER & 0x2) && (NetlinkArea->reg.IIR & 0xF) == 0x2)
158             NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1;
159          return NetlinkArea->reg.IIR;
160       }
161       case 0x9500D: // Line Control Register
162       {
163          return NetlinkArea->reg.LCR;
164       }
165       case 0x95011: // Modem Control Register
166       {
167          return NetlinkArea->reg.MCR;
168       }
169       case 0x95015: // Line Status Register
170       {
171          return NetlinkArea->reg.LSR;
172       }
173       case 0x95019: // Modem Status Register
174       {
175          // If interrupt has been triggered because of MSR change, reset it
176          if ((NetlinkArea->reg.IER & 0x8) && (NetlinkArea->reg.IIR & 0xF) == 0)
177             NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1;
178          ret = NetlinkArea->reg.MSR;
179          NetlinkArea->reg.MSR &= 0xF0;
180          return ret;
181       }
182       case 0x9501D: // Scratch
183       {
184          return NetlinkArea->reg.SCR;
185       }
186       default:
187          break;
188    }
189 
190    NETLINK_LOG("Unimplemented Netlink byte read: %08X\n", addr);
191    return 0xFF;
192 }
193 
194 //////////////////////////////////////////////////////////////////////////////
195 
196 /*!
197 * Write AT response to transmit buffer
198  * @param[in]  string String to write
199  */
NetlinkDoATResponse(const char * string)200 static void FASTCALL NetlinkDoATResponse(const char *string)
201 {
202    strcpy((char *)&NetlinkArea->outbuffer[NetlinkArea->outbufferend], string);
203    NetlinkArea->outbufferend += (u32)strlen(string);
204    NetlinkArea->outbuffersize += (u32)strlen(string);
205 }
206 
207 //////////////////////////////////////////////////////////////////////////////
208 
209 /*!
210  * Parse AT parameter value
211  * @param[in]  val    Character
212  * @param[out] offset buffer offset
213  * \return integer value for parameter
214  */
NetlinkFetchATParameter(u8 val,u32 * offset)215 static int FASTCALL NetlinkFetchATParameter(u8 val, u32 *offset)
216 {
217    if (val >= '0' && val <= '9')
218    {
219       (*offset)++;
220       return (val - 0x30);
221    }
222    else
223       return 0;
224 }
225 
226 //////////////////////////////////////////////////////////////////////////////
227 
228 /*!
229  * Checks if there's data in the receive buffer, and if so sends interrupt
230  */
NetlinkUpdateReceivedDataInt()231 void NetlinkUpdateReceivedDataInt()
232 {
233    if (NetlinkArea->outbuffersize > 0)
234    {
235       // Set Data available bit in LSR
236       NetlinkArea->reg.LSR |= 0x01;
237 
238       // Trigger Interrrupt
239       NetlinkArea->reg.IIR = 0x4;
240       ScuSendExternalInterrupt12();
241   }
242 }
243 
244 //////////////////////////////////////////////////////////////////////////////
245 
246 /*!
247  * Removes character from string
248  * @param[in]  str String to change
249  * @param[in]  c   Character to remove
250  */
remove_all_chars(char * str,char c)251 void remove_all_chars(char* str, char c)
252 {
253    char *pr = str, *pw = str;
254    while (*pr) {
255       *pw = *pr++;
256       pw += (*pw != c);
257    }
258    *pw = '\0';
259 }
260 
261 //////////////////////////////////////////////////////////////////////////////
262 
263 /*!
264  * Writes byte data to register from Netlink address space
265  * @param[in]  sh   SH2 context
266  * @param[in]  addr Address to write
267  * @param[in]  val  Data to write
268  */
NetlinkWriteByte(SH2_struct * sh,u32 addr,u8 val)269 void FASTCALL NetlinkWriteByte(SH2_struct *sh, u32 addr, u8 val)
270 {
271    switch (addr & 0xFFFFF)
272    {
273       case 0x2503D: // ???
274       {
275          return;
276       }
277       case 0x95001: // Transmitter Holding Buffer/Divisor Latch Low Byte
278       {
279          if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch Low Byte
280          {
281             NetlinkArea->reg.DLL = val;
282          }
283          else // Transmitter Holding Buffer
284          {
285             if (NetlinkArea->thb_write_time == 0xFFFFFFFF)
286                NetlinkArea->thb_write_time = 0;
287 
288             if (val == '+')
289             {
290                // Possible escape sequence?
291                if (NetlinkArea->escape_count == 0 &&
292                   NetlinkArea->thb_write_time >= 1000000)
293                {
294                   // Start of sequence
295                   NetlinkArea->escape_count++;
296                }
297                else if (NetlinkArea->escape_count >= 1)
298                {
299                   // Middle/possible tail
300                   NetlinkArea->escape_count++;
301                }
302             }
303             else
304                NetlinkArea->escape_count = 0;
305 
306             NetlinkArea->inbuffer[NetlinkArea->inbufferend] = val;
307             NetlinkArea->thb_write_time = 0;
308             NetlinkArea->inbufferend++;
309             if (NetlinkArea->inbufferend == NETLINK_BUFFER_SIZE)
310             {
311                NetlinkArea->inbufferstart=0;
312                NetlinkArea->inbufferend=1;
313             }
314             NetlinkArea->inbuffersize++;
315 
316             // If interrupt has been triggered because THB is empty, reset it
317             if ((NetlinkArea->reg.IER & 0x2) && (NetlinkArea->reg.IIR & 0xF) == 0x2)
318                NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1;
319 
320             if (NetlinkArea->modemstate == NL_MODEMSTATE_COMMAND)
321             {
322 
323                if (val == 0x0D &&
324                    (strncmp((char *)&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], "AT", 2) == 0 ||
325                     strncmp((char *)&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], "at", 2) == 0)) // fix me
326                {
327                   u32 i=NetlinkArea->inbufferstart+2;
328                   enum NL_RESULTCODE resultcode=NL_RESULTCODE_OK;
329                   int parameter;
330 
331                   NETLINK_LOG("Program issued %s\n", NetlinkArea->inbuffer);
332 
333                   // If echo is enabled, do it
334                   if (NetlinkArea->isechoenab)
335                      NetlinkDoATResponse((char *)NetlinkArea->inbuffer);
336 
337                   // Handle AT command
338                   while(NetlinkArea->inbuffer[i] != 0xD)
339                   {
340                      switch (toupper(NetlinkArea->inbuffer[i]))
341                      {
342                         case ' ':
343                            // Whitespace
344                            break;
345                         case '%':
346                            break;
347                         case '&':
348                            // Figure out second part of command
349                            i++;
350 
351                            switch (toupper(NetlinkArea->inbuffer[i]))
352                            {
353                               case 'C':
354                                  // Data Carrier Detect Options
355                                  NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
356                                  break;
357                               case 'D':
358                                  // Data Terminal Ready Options
359                                  NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
360                                  break;
361                               case 'F':
362                                  // Factory reset
363                                  NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
364                                  break;
365                               case 'K':
366                                  // Local Flow Control Options
367                                  NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
368                                  break;
369                               case 'Q':
370                                  // Communications Mode Options
371                                  NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
372                                  break;
373                               case 'S':
374                                  // Data Set Ready Options
375                                  NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
376                                  break;
377                               default: break;
378                            }
379                            break;
380                         case 'S':
381                         {
382                            // Status Registers
383                            int reg;
384                            char *str;
385                            char *inbuffer = (char *) NetlinkArea->inbuffer;
386 
387                            i++;
388 
389                            reg = strtoul(inbuffer+i, &str, 10);
390                            i = str-inbuffer;
391 
392                            if (inbuffer[i] == '=')
393                            {
394                               NetlinkArea->reg.SREG[reg & 0xFF] = strtoul(inbuffer+i+1, &str, 10);
395                               i = str-inbuffer;
396                            }
397 
398                            i-=1;
399 
400                            switch (reg)
401                            {
402                               case 7:
403                                  // Wait Time for Carrier, Silence, or Dial Tone
404                                  NetlinkArea->connect_timeout = NetlinkArea->reg.SREG[reg] * 1000000;
405                                  break;
406                               default: break;
407                            }
408                            break;
409                         }
410                         case ')':
411                         case '*':
412                         case ':':
413                         case '?':
414                         case '@':
415                            break;
416                         case '\\':
417                            i++;
418 
419                            if (toupper(NetlinkArea->inbuffer[i]) == 'N')
420                            {
421                               // linefeed
422                            }
423 
424                            break;
425                         case 'A':
426                            // Answer Command(no other commands should follow)
427                            NETLINK_LOG("Starting answer\n");
428                            break;
429                         case 'D':
430                         {
431                            // Dial Command
432                            char *p;
433                            int j;
434                            char *inbuffer = (char *) NetlinkArea->inbuffer;
435 
436                            NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECT;
437 
438                            NetlinkArea->internet_enable = 0;
439 
440                            if ((p = strchr(inbuffer+i+2, '*')) != NULL)
441                            {
442                               // Fetch IP
443                               char ipstring[45];
444 
445                               sscanf(p+1, "%[^\r]\r", ipstring);
446 
447                               // replace ',' with '.'
448                               for (j = 0; ipstring[j] != '\0'; j++)
449                                  if (ipstring[j] == ',')
450                                     ipstring[j] = '.';
451 
452                               // Get port string if necessary
453                               if ((p = strchr(ipstring, '*')) != NULL)
454                               {
455                                  p[0] = '\0';
456                                  strcpy(NetlinkArea->portstring, p+1);
457                               }
458                               strcpy(NetlinkArea->ipstring, ipstring);
459                            }
460                            else
461                            {
462                               // If we're using Sega's old network, just assume we're using internet mode
463                               char number[45];
464 
465                               sscanf(inbuffer+i+2, "%[^\r]\r", number);
466                               remove_all_chars(number, '-');
467                               if (strcmp(number, "18007798852") == 0 ||
468                                   strcmp(number, "8007798852") == 0)
469                                  NetlinkArea->internet_enable = 1;
470                            }
471 
472                            if (!NetlinkArea->internet_enable)
473                            {
474 #ifdef USESOCKET
475                               NetworkConnect(NetlinkArea->ipstring, NetlinkArea->portstring);
476 #endif
477                               NetlinkArea->connect_time = 0;
478                               NETLINK_LOG("Starting dial %s\n", NetlinkArea->ipstring);
479                            }
480 
481                            i = strchr(inbuffer+i, '\r')-inbuffer-1;
482                            break;
483                         }
484                         case 'E':
485                            // Command State Character Echo Selection
486 
487                            parameter = NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
488 
489                            // Parameter can only be 0 or 1
490                            if (parameter < 2)
491                               NetlinkArea->isechoenab = parameter;
492                            else
493                               resultcode = NL_RESULTCODE_ERROR;
494 
495                            break;
496                         case 'I':
497                            // Internal Memory Tests
498                            switch(NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i))
499                            {
500                               case 0:
501                                  NetlinkDoATResponse("\r\n28800\r\n");
502                                  break;
503                               default: break;
504                            }
505                            break;
506                         case 'L':
507                            // Speaker Volume Level Selection
508                            NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
509                            break;
510                         case 'M':
511                            // Speaker On/Off Selection
512                            NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
513                            break;
514                         case 'V':
515                            // Result Code Format Options
516                            NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
517                            break;
518                         case 'W':
519                            // Negotiation Progress Message Selection
520                            NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i);
521                            break;
522                         default:
523                            NETLINK_LOG("Unsupported AT command %c", NetlinkArea->inbuffer[i]);
524                            break;
525                      }
526 
527                      i++;
528                   }
529 
530                   switch (resultcode)
531                   {
532                      case NL_RESULTCODE_OK: // OK
533                         NetlinkDoATResponse("\r\nOK\r\n");
534                         break;
535                      case NL_RESULTCODE_CONNECT: // CONNECT
536                         NetlinkDoATResponse("\r\nCONNECT\r\n");
537                         break;
538                      case NL_RESULTCODE_RING: // RING
539                         NetlinkDoATResponse("\r\nRING\r\n");
540                         break;
541                      case NL_RESULTCODE_NOCARRIER: // NO CARRIER
542                         NetlinkDoATResponse("\r\nNO CARRIER\r\n");
543                         break;
544                      case NL_RESULTCODE_ERROR: // ERROR
545                         NetlinkDoATResponse("\r\nERROR\r\n");
546                         break;
547                      case NL_RESULTCODE_CONNECT1200: // CONNECT 1200
548                         NetlinkDoATResponse("\r\nCONNECT 1200\r\n");
549                         break;
550                      case NL_RESULTCODE_NODIALTONE: // NO DIALTONE
551                         NetlinkDoATResponse("\r\nNO DIALTONE\r\n");
552                         break;
553                      case NL_RESULTCODE_BUSY: // BUSY
554                         NetlinkDoATResponse("\r\nBUSY\r\n");
555                         break;
556                      case NL_RESULTCODE_NOANSWER: // NO ANSWER
557                         NetlinkDoATResponse("\r\nNO ANSWER\r\n");
558                         break;
559                      default: break;
560                   }
561 
562                   memset((void *) NetlinkArea->inbuffer, 0, NetlinkArea->inbuffersize);
563                   NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0;
564 
565                   if (NetlinkArea->outbuffersize > 0)
566                   {
567                      // Set Data available bit in LSR
568                      NetlinkArea->reg.LSR |= 0x01;
569 
570                      // Trigger Interrrupt
571                      NetlinkArea->reg.IIR = 0x4;
572                      ScuSendExternalInterrupt12();
573                   }
574                }
575             }
576             else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN1 &&
577                      NetlinkArea->modemstate == NL_MODEMSTATE_DATA &&
578                      val == 0x0D)
579             {
580                // Internet login name
581                NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN2;
582                NETLINK_LOG("login response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart);
583                NetlinkDoATResponse("\r\npassword:");
584                NetlinkUpdateReceivedDataInt();
585             }
586             else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN2 &&
587                NetlinkArea->modemstate == NL_MODEMSTATE_DATA &&
588                val == 0x0D)
589             {
590                // Internet password
591                NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN3;
592                NETLINK_LOG("password response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart);
593                NetlinkDoATResponse("\r\n$");
594                NetlinkUpdateReceivedDataInt();
595             }
596             else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN3 &&
597                NetlinkArea->modemstate == NL_MODEMSTATE_DATA &&
598                val == 0x0D)
599             {
600                // Internet password
601                NETLINK_LOG("shell response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart);
602                NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECTED;
603             }
604          }
605 
606          return;
607       }
608       case 0x95005: // Interrupt Enable Register/Divisor Latch High Byte
609       {
610          if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch High Byte
611          {
612             NetlinkArea->reg.DLM = val;
613          }
614          else // Interrupt Enable Register
615          {
616             NetlinkArea->reg.IER = val;
617          }
618 
619          return;
620       }
621       case 0x95009: // FIFO Control Register
622       {
623          NetlinkArea->reg.FCR = val;
624 
625          if (val & 0x1)
626             // set FIFO enabled bits
627             NetlinkArea->reg.IIR |= 0xC0;
628          else
629             // clear FIFO enabled bits
630             NetlinkArea->reg.IIR &= ~0xC0;
631 
632          return;
633       }
634       case 0x9500D: // Line Control Register
635       {
636          NetlinkArea->reg.LCR = val;
637          return;
638       }
639       case 0x95011: // Modem Control Register
640       {
641          NetlinkArea->reg.MCR = val;
642          return;
643       }
644       case 0x95019: // Modem Status Register(read-only)
645          return;
646       case 0x9501D: // Scratch
647       {
648          NetlinkArea->reg.SCR = val;
649          return;
650       }
651       default:
652          break;
653    }
654 
655    NETLINK_LOG("Unimplemented Netlink byte write: %08X\n", addr);
656 }
657 
658 //////////////////////////////////////////////////////////////////////////////
659 
660 /*!
661  * Initialize Netlink
662  * @param[in]  ip   IP to use for connecting or receiving
663  * @param[in]  port Port to use for connecting or receive
664  * \return 0
665  */
NetlinkInit(const char * ip,const char * port)666 int NetlinkInit(const char *ip, const char *port)
667 {
668    if ((NetlinkArea = malloc(sizeof(Netlink))) == NULL)
669    {
670       Cs2Area->carttype = CART_NONE;
671       YabSetError(YAB_ERR_CANNOTINIT, (void *)"Netlink");
672       return 0;
673    }
674 
675    memset((void *) NetlinkArea->inbuffer, 0, NETLINK_BUFFER_SIZE);
676    memset((void *) NetlinkArea->outbuffer, 0, NETLINK_BUFFER_SIZE);
677 
678    NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0;
679    NetlinkArea->inbufferupdate = 0;
680    NetlinkArea->outbufferstart = NetlinkArea->outbufferend = NetlinkArea->outbuffersize = 0;
681    NetlinkArea->outbufferupdate = 0;
682 
683    NetlinkArea->isechoenab = 1;
684    NetlinkArea->cycles = 0;
685    NetlinkArea->thb_write_time = 0xFFFFFFFF;
686    NetlinkArea->modemstate = NL_MODEMSTATE_COMMAND;
687    NetlinkArea->escape_count = 0;
688 
689    NetlinkArea->reg.RBR = 0x00;
690    NetlinkArea->reg.IER = 0x00;
691    NetlinkArea->reg.DLL = 0x00;
692    NetlinkArea->reg.DLM = 0x00;
693    NetlinkArea->reg.IIR = 0x01;
694 //      NetlinkArea->reg.FCR = 0x??; // have no idea
695    NetlinkArea->reg.LCR = 0x00;
696    NetlinkArea->reg.MCR = 0x00;
697    NetlinkArea->reg.LSR = 0x60;
698    NetlinkArea->reg.MSR = 0x30;
699    NetlinkArea->reg.SCR = 0x01;
700 
701    NetlinkArea->reg.SREG[7] = 50;
702    NetlinkArea->connect_timeout = NetlinkArea->reg.SREG[7] * 1000000;
703 
704    if (ip == NULL || strcmp(ip, "") == 0)
705       // Use Loopback ip
706       sprintf(NetlinkArea->ipstring, "127.0.0.1");
707 	else
708 		strcpy(NetlinkArea->ipstring, ip);
709 
710 	if (port == NULL || strcmp(port, "") == 0)
711 		// Default port
712       sprintf(NetlinkArea->portstring, "1337");
713    else
714    {
715       size_t port_len = strlen(port);
716       if (port_len >= 6)
717       {
718          YabSetError(YAB_ERR_OTHER, "Netlink port is too long");
719          return 0;
720       }
721       strcpy(NetlinkArea->portstring, port);
722    }
723 
724 #ifdef USESOCKET
725    return NetworkInit(NetlinkArea->portstring);
726 #else
727    return 0;
728 #endif
729 }
730 
731 //////////////////////////////////////////////////////////////////////////////
732 
733 /*!
734  * Deinitialize Netlink
735  */
NetlinkDeInit(void)736 void NetlinkDeInit(void)
737 {
738 #ifdef USESOCKET
739    NetworkDeInit();
740 #endif
741 
742    if (NetlinkArea)
743       free(NetlinkArea);
744 }
745 
746 //////////////////////////////////////////////////////////////////////////////
747 
748 /*!
749  * Netlink execution cycle
750  * @param[in]  timing usec to execute
751  * \return 0
752  */
NetlinkExec(u32 timing)753 void NetlinkExec(u32 timing)
754 {
755    NetlinkArea->cycles += timing;
756    NetlinkArea->connect_time += timing;
757    if (NetlinkArea->thb_write_time != 0xFFFFFFFF)
758       NetlinkArea->thb_write_time += timing;
759 
760    if (NetlinkArea->cycles >= 20000)
761    {
762       NetlinkArea->cycles -= 20000;
763 
764       if (NetlinkArea->escape_count == 3 && NetlinkArea->thb_write_time >= 1000000)
765       {
766          // Switch back to command mode
767          NETLINK_LOG("Detected escape sequence, switching back to command mode\n");
768          NetlinkArea->modemstate = NL_MODEMSTATE_COMMAND;
769       }
770 
771       switch(NetlinkArea->connectstatus)
772       {
773          case NL_CONNECTSTATUS_IDLE:
774          {
775 #ifdef USESOCKET
776             if (NetworkWaitForConnect() == 0)
777             {
778                NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECTED;
779                NetlinkArea->modemstate = NL_MODEMSTATE_DATA;
780 
781                // This is probably wrong, but let's give it a try anyways
782                NetlinkDoATResponse("\r\nRING\r\n\r\nCONNECT\r\n");
783                NetlinkMSRChange(0x08, 0x00);
784                NetlinkUpdateReceivedDataInt();
785 
786                NETLINK_LOG("Connected via listener\n");
787             }
788 #endif
789             break;
790          }
791          case NL_CONNECTSTATUS_CONNECT:
792          {
793 #ifdef USESOCKET
794             if (NetlinkArea->internet_enable || NetworkWaitForConnect() == 0)
795             {
796                NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECTED;
797                NetlinkArea->modemstate = NL_MODEMSTATE_DATA;
798 
799                NetlinkDoATResponse("\r\nCONNECT 28800\r\n");
800                NetlinkMSRChange(0x08, 0x00);
801                NetlinkUpdateReceivedDataInt();
802                NETLINK_LOG("Connected via remote ip connect\n");
803 
804                if (NetlinkArea->internet_enable)
805                {
806                   NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN1;
807                   NetlinkDoATResponse("\r\nlogin:");
808                }
809             }
810             else if (NetlinkArea->connect_time >= NetlinkArea->connect_timeout)
811             {
812                // Kill connect attempt
813                NetworkStopConnect();
814                NetlinkDoATResponse("\r\nNO ANSWER\r\n");
815                NetlinkUpdateReceivedDataInt();
816                NetlinkArea->connectstatus = NL_CONNECTSTATUS_IDLE;
817             }
818 #endif
819             break;
820          }
821          case NL_CONNECTSTATUS_CONNECTED:
822          {
823 #ifdef USESOCKET
824 
825             if (NetlinkArea->outbufferupdate)
826             {
827                NetlinkMSRChange(0x08, 0x00);
828                NetlinkUpdateReceivedDataInt();
829 
830                //NETLINK_LOG("Received %d byte(s) from external source\n", NetlinkArea->bytes_read);
831                NetlinkArea->outbufferupdate = 0;
832             }
833 #endif
834 
835             break;
836          }
837          default: break;
838       }
839    }
840 }
841 
842 //////////////////////////////////////////////////////////////////////////////
843 #ifdef USESOCKET
844 /*!
845  * Thread for communicating to another instance of Yabause for Netlink
846  * @param[in]  data netlink_thread structure
847  */
netlink_client(void * data)848 void netlink_client(void *data)
849 {
850    netlink_thread *client=(netlink_thread *)data;
851 
852    netlink_client_thread_running = 1;
853 
854    while (netlink_client_thread_running)
855    {
856       int bytes;
857       if (YabSockSelect(client->sock, 1, 1) != 0)
858       {
859          continue;
860       }
861 
862       if (NetlinkArea->modemstate == NL_MODEMSTATE_DATA && NetlinkArea->inbuffersize > 0 && YabSockIsWriteSet(client->sock) && NetlinkArea->thb_write_time > 1000)
863       {
864          //NETLINK_LOG("Sending to external source...");
865 
866          // Send via network connection
867          if ((bytes = YabSockSend(client->sock, (void *) &NetlinkArea->inbuffer[NetlinkArea->inbufferstart], NetlinkArea->inbufferend-NetlinkArea->inbufferstart, 0)) >= 0)
868          {
869             //NETLINK_LOG("Successfully sent %d byte(s)\n", bytes);
870             if (NetlinkArea->inbufferend > bytes)
871             {
872                NetlinkArea->inbufferstart += bytes;
873                NetlinkArea->inbuffersize -= bytes;
874             }
875             else
876                NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0;
877             NetlinkArea->inbufferupdate = 1;
878          }
879          else
880          {
881             //NETLINK_LOG("failed.\n");
882          }
883       }
884 
885       if (YabSockIsReadSet(client->sock))
886       {
887          //NETLINK_LOG("Data is ready from external source...");
888          if ((bytes = YabSockReceive(client->sock, (void *) &NetlinkArea->outbuffer[NetlinkArea->outbufferend], sizeof(NetlinkArea->outbuffer)-1-NetlinkArea->outbufferend, 0)) > 0)
889          {
890             //NETLINK_LOG("Successfully received %d byte(s)\n", bytes);
891             NetlinkArea->outbufferend += bytes;
892             NetlinkArea->outbuffersize += bytes;
893             NetlinkArea->outbufferupdate = 1;
894          }
895       }
896       //YabThreadSleep();
897    }
898 
899    free(data);
900 }
901 
902 //////////////////////////////////////////////////////////////////////////////
903 
904 /*!
905  * Stop Netlink Client thread
906  */
NetworkStopClient()907 static void NetworkStopClient()
908 {
909    if (netlink_client_thread_running)
910    {
911       if (NetlinkArea->clientsocket != -1)
912          YabSockCloseSocket(NetlinkArea->clientsocket);
913       NetlinkArea->clientsocket = -1;
914 
915       netlink_client_thread_running = 0;
916       YabThreadWait(YAB_THREAD_NETLINKCLIENT);
917    }
918 
919 }
920 
921 //////////////////////////////////////////////////////////////////////////////
922 
923 /*!
924  * Thread for listening for connections from other Yabause clients
925  * @param[in]  data YabSock structure
926  */
netlink_listener(void * data)927 void netlink_listener(void *data)
928 {
929    YabSock Listener=(YabSock)(pointer)data;
930    netlink_thread *client;
931 
932    netlink_listener_thread_running = 1;
933 
934    while(netlink_listener_thread_running)
935    {
936       NetlinkArea->clientsocket = YabSockAccept(Listener);
937       if (NetlinkArea->clientsocket == -1)
938       {
939          perror("accept failed\n");
940          continue;
941       }
942 
943       client = malloc(sizeof(netlink_thread));
944       client->sock = NetlinkArea->clientsocket;
945       YabThreadStart(YAB_THREAD_NETLINKCLIENT, netlink_client, (void *)client);
946    }
947 
948    return;
949 }
950 
951 //////////////////////////////////////////////////////////////////////////////
952 
953 /*!
954  * Stop Netlink Listener thread
955  */
NetworkStopListener()956 static void NetworkStopListener()
957 {
958    if (netlink_listener_thread_running)
959    {
960       if (NetlinkArea->listensocket != -1)
961          YabSockCloseSocket(NetlinkArea->listensocket);
962       NetlinkArea->listensocket = -1;
963       netlink_listener_thread_running = 0;
964       YabThreadWait(YAB_THREAD_NETLINKLISTENER);
965    }
966 }
967 
968 //////////////////////////////////////////////////////////////////////////////
969 
970 /*!
971  * Restart Netlink Listener thread
972  * @param[in]  port Port to listen on
973  */
NetworkRestartListener(int port)974 int NetworkRestartListener(int port)
975 {
976    int ret;
977    if ((ret = YabSockListenSocket(port, &NetlinkArea->listensocket)) != 0)
978       return ret;
979 
980    YabThreadStart(YAB_THREAD_NETLINKLISTENER, netlink_listener, (void *)(pointer)NetlinkArea->listensocket);
981    return ret;
982 }
983 
984 //////////////////////////////////////////////////////////////////////////////
985 
986 /*!
987  * Initialize Networking code
988  * @param[in]  port Port to listen on as a string
989  * \return 0 or < 0 on error
990  */
NetworkInit(const char * port)991 static int NetworkInit(const char *port)
992 {
993    int ret;
994 
995    YabSockInit();
996 
997    NetlinkArea->clientsocket = NetlinkArea->listensocket = NetlinkArea->connectsocket = -1;
998 
999    if (ret = NetworkRestartListener(atoi(port)) != 0)
1000       return ret;
1001 
1002    NetlinkArea->connectstatus = NL_CONNECTSTATUS_IDLE;
1003 
1004    return 0;
1005 }
1006 
1007 //////////////////////////////////////////////////////////////////////////////
1008 
1009 /*!
1010  * Networking connect thread
1011  * @param[in]  data netlink_thread structure
1012  */
netlink_connect(void * data)1013 void netlink_connect(void *data)
1014 {
1015    netlink_thread *connect=(netlink_thread *)data;
1016 
1017    netlink_connect_thread_running = 1;
1018 
1019    while(netlink_connect_thread_running)
1020    {
1021       if (YabSockConnectSocket(connect->ip, connect->port, &connect->sock) == 0)
1022       {
1023          NetlinkArea->connectsocket = connect->sock;
1024 
1025          YabThreadStart(YAB_THREAD_NETLINKCLIENT, netlink_client, (void *)connect);
1026          return;
1027       }
1028       else
1029          YabThreadYield();
1030    }
1031 
1032    NetworkRestartListener(connect->port);
1033    free(data);
1034 }
1035 
1036 //////////////////////////////////////////////////////////////////////////////
1037 
1038 /*!
1039  * Stop Network Connect thread
1040  */
NetworkStopConnect()1041 static void NetworkStopConnect()
1042 {
1043    if (netlink_connect_thread_running)
1044    {
1045       if (NetlinkArea->connectsocket != -1)
1046          YabSockCloseSocket(NetlinkArea->connectsocket);
1047       NetlinkArea->connectsocket = -1;
1048 
1049       netlink_connect_thread_running = 0;
1050       YabThreadWait(YAB_THREAD_NETLINKCONNECT);
1051    }
1052 
1053 }
1054 
1055 //////////////////////////////////////////////////////////////////////////////
1056 
1057 /*!
1058  * Start Networking Connect thread
1059  * @param[in]  ip   IP to connect to as a string
1060  * @param[in]  port Port to connect to as a string
1061  */
NetworkConnect(const char * ip,const char * port)1062 static void NetworkConnect(const char *ip, const char *port)
1063 {
1064    netlink_thread *connect;
1065    connect = malloc(sizeof(netlink_thread));
1066    strcpy(connect->ip, ip);
1067    connect->port = atoi(port);
1068 
1069    NetworkStopListener();
1070    YabThreadStart(YAB_THREAD_NETLINKCONNECT, netlink_connect, connect);
1071 }
1072 
1073 //////////////////////////////////////////////////////////////////////////////
1074 
1075 /*!
1076  * Check to see if yabause clients are connected
1077  */
NetworkWaitForConnect()1078 static int NetworkWaitForConnect()
1079 {
1080    if (netlink_client_thread_running)
1081       return 0;
1082    else
1083       return -1;
1084 }
1085 
1086 //////////////////////////////////////////////////////////////////////////////
1087 
1088 /*!
1089  * Deinitialize Networking code
1090  */
NetworkDeInit(void)1091 static void NetworkDeInit(void)
1092 {
1093    NetworkStopListener();
1094    NetworkStopConnect();
1095    NetworkStopClient();
1096 
1097    YabSockDeInit();
1098 }
1099 
1100 //////////////////////////////////////////////////////////////////////////////
1101 
1102 #endif
1103