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