1 /*
2  *  KCemu -- The emulator for the KC85 homecomputer series and much more.
3  *  Copyright (C) 1997-2010 Torsten Paul
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <signal.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/poll.h>
29 #include <sys/un.h>
30 #include <sys/signal.h>
31 
32 #include "kc/system.h"
33 
34 #include "kc/kc.h"
35 #include "kc/z80.h"
36 #include "kc/mod_v24.h"
37 #include "kc/prefs/prefs.h"
38 
39 #include "libdbg/dbg.h"
40 
41 using namespace std;
42 
43 static ModuleV24 *self;
44 
ModuleV24(ModuleV24 & tmpl)45 ModuleV24::ModuleV24(ModuleV24 &tmpl) :
46   ModuleInterface(tmpl.get_name(), tmpl.get_id(), tmpl.get_type()),
47   InterfaceCircuit("ModuleV24")
48 {
49   _reg[A] = 0;
50   _reg[B] = 0;
51   _data_in[A] = 0;
52   _data_in[B] = 0;
53   _reg_rd[A][0] = 0xff;
54   _reg_rd[A][1] = 0xff;
55   _reg_rd[A][2] = 0xff;
56   _reg_rd[B][0] = 0xff;
57   _reg_rd[B][1] = 0xff;
58   _reg_rd[B][2] = 0xff;
59 
60   _irq_active[A] = 0;
61   _irq_active[B] = 0;
62   _irq_pending[A] = 0;
63   _irq_pending[B] = 0;
64 
65   _fd_in[A] = 0;
66   _fd_in[B] = 0;
67   _fd_out[A] = 0;
68   _fd_out[B] = 0;
69   _in_buf_ptr = 0;
70   _socket_name = 0;
71 }
72 
ModuleV24(const char * name,byte_t id)73 ModuleV24::ModuleV24(const char *name, byte_t id) :
74   ModuleInterface(name, id, KC_MODULE_KC_85_3),
75   InterfaceCircuit("ModuleV24")
76 {
77   _fd_in[A] = 0;
78   _fd_in[B] = 0;
79   _fd_out[A] = 0;
80   _fd_out[B] = 0;
81   _in_buf_ptr = 0;
82   _socket_name = 0;
83 }
84 
~ModuleV24(void)85 ModuleV24::~ModuleV24(void)
86 {
87   close_device();
88 }
89 
90 byte_t
in(word_t addr)91 ModuleV24::in(word_t addr)
92 {
93   byte_t val;
94   int port = addr & 0xff;
95 
96   val = 0xff;
97   switch (port)
98     {
99     case 0x80:
100       return get_id();
101       break;
102     case 0x08:
103       /* SIO channel A - data */
104       val = _data_in[A];
105       DBG(2, form("KCemu/ModuleV24/in/data/A",
106                   "%04x: in_data: <- sio A 08h <- %0xh\n",
107                   z80->getPC(), val));
108       break;
109     case 0x09:
110       /* SIO channel B - data */
111       val = _data_in[B];
112       DBG(2, form("KCemu/ModuleV24/in/data/B",
113                   "%04x: in_data: <- sio B 08h <- %0xh\n",
114                   z80->getPC(), val));
115       break;
116     case 0x0a:
117       /* SIO channel A - control */
118       val = in_reg(A);
119       break;
120     case 0x0b:
121       /* SIO channel B - control */
122       val = in_reg(B);
123       break;
124     case 0x0c:
125       val = 0xff;
126       DBG(2, form("KCemu/ModuleV24/in",
127                   "%04xh: in_reg:  <- ctc 0 0ch <- %0xh\n",
128                   z80->getPC(), val));
129       break;
130     case 0x0d:
131       val = 0xff;
132       DBG(2, form("KCemu/ModuleV24/in",
133                   "%04x: in_reg:  <- ctc 1 0dh <- %0xh\n",
134                   z80->getPC(), val));
135       break;
136     case 0x0e:
137       val = 0xff;
138       DBG(2, form("KCemu/ModuleV24/in",
139                   "%04x: in_reg:  <- ctc 2 0eh <- %0xh\n",
140                   z80->getPC(), val));
141       break;
142     case 0x0f:
143       val = 0xff;
144       DBG(2, form("KCemu/ModuleV24/in",
145                   "%04x: in_reg:  <- ctc 3 0eh <- %0xh\n",
146                   z80->getPC(), val));
147       break;
148     }
149 
150   DBG(3, form("KCemu/ModuleV24/in",
151               "%04x: %s: %04x, %02x\n",
152               z80->getPC(), __PRETTY_FUNCTION__, addr, val));
153   return val;
154 }
155 
156 void
out(word_t addr,byte_t val)157 ModuleV24::out(word_t addr, byte_t val)
158 {
159   int port = addr & 0xff;
160 
161   DBG(3, form("KCemu/ModuleV24/out",
162               "%s: %04x, %02x\n",
163               __PRETTY_FUNCTION__, addr, val));
164   switch (port)
165     {
166     case 0x80:
167       if (((_val & 1) ^ (val & 1)) != 1) return;
168       if (val & 1)
169         {
170           open_device();
171           _portg = ports->register_ports(get_name(), 8, 6, this, (addr >> 8));
172         }
173       else
174         close_device();
175 
176       _val = val;
177       break;
178     case 0x08:
179       /* SIO channel A - data */
180       DBG(2, form("KCemu/ModuleV24/out/data/A",
181                   "out_data: sio A 08h: %0xh (io_type = %d)\n",
182                   val, _io_type));
183       if (_fd_out[A] && (_io_type != IO_NONE))
184         if (_io_type == IO_FILE)
185           write(_fd_out[A], &val, 1);
186         else
187           send(_fd_out[A], &val, 1, 0);
188       break;
189     case 0x09:
190       /* SIO channel B - data */
191       DBG(2, form("KCemu/ModuleV24/out/data/B",
192                   "out_data: sio B 09h: %0xh (io_type = %d)\n",
193                   val, _io_type));
194       if (_fd_out[B] && (_io_type != IO_NONE))
195         if (_io_type == IO_FILE)
196           write(_fd_out[B], &val, 1);
197         else
198           send(_fd_out[B], &val, 1, 0);
199       break;
200     case 0x0a:
201       /* SIO channel A - control */
202       out_reg(A, val);
203       break;
204     case 0x0b:
205       /* SIO channel B - control */
206       out_reg(B, val);
207       break;
208     case 0x0c:
209       DBG(2, form("KCemu/ModuleV24/out/ctc/0",
210                   "out_reg: -> ctc 0 0ch -> %0xh\n",
211                   val));
212       break;
213     case 0x0d:
214       DBG(2, form("KCemu/ModuleV24/out/ctc/1",
215                   "out_reg: -> ctc 1 0dh -> %0xh\n",
216                   val));
217       break;
218     case 0x0e:
219       DBG(2, form("KCemu/ModuleV24/out/ctc/2",
220                   "out_reg: -> ctc 2 0eh -> %0xh\n",
221                   val));
222       break;
223     case 0x0f:
224       DBG(2, form("KCemu/ModuleV24/out/ctc/3",
225                   "out_reg: -> ctc 3 0eh -> %0xh\n",
226                   val));
227       break;
228     }
229 }
230 
231 byte_t
in_reg(int c)232 ModuleV24::in_reg(int c)
233 {
234   DBG(2, form("KCemu/ModuleV24/in_reg/raw",
235               "in_reg:  <- [%d] %02xh <- %d\n",
236               c, _reg_rd[c][_reg[c]], _reg[c]));
237   return _reg_rd[c][_reg[c]];
238 }
239 
240 void
out_reg(int c,byte_t val)241 ModuleV24::out_reg(int c, byte_t val)
242 {
243   static const char *wr0_0[] = {
244     "",
245     " next write to register 1\n",
246     " next write to register 2\n",
247     " next write to register 3\n",
248     " next write to register 4\n",
249     " next write to register 5\n",
250     " next write to register 6\n",
251     " next write to register 7\n",
252   };
253   static const char *wr0_1[] = {
254     "",
255     " send break (SDLC)\n",
256     " reset external and status interrupts\n",
257     " reset channel\n",
258     " reset receive interrupt on first character\n",
259     " reset send interrupt\n",
260     " reset error condition\n",
261     " interrupt return (channel A)\n",
262   };
263   static const char *wr0_2[] = {
264     "",
265     " reset receiver CRC\n",
266     " reset sender CRC\n",
267     " reset CRC/SYNC status memory\n",
268   };
269 
270   DBG(2, form("KCemu/ModuleV24/out_reg/raw",
271               "out_reg: -> [%d] %02xh -> %d\n",
272               c, val, _reg[c]));
273   _reg_wr[c][_reg[c]] = val;
274   switch (_reg[c])
275     {
276     case 0:
277       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr0",
278                   "wr0: [%d] %02xh -> %d\n%s%s%s",
279                   c, val, _reg[c],
280                   wr0_0[val & 7],
281                   wr0_1[(val >> 3) & 7],
282                   wr0_2[(val >> 6) & 3]));
283           break;
284     case 1:
285       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr1",
286                   "wr1: [%d] %02xh -> %d\n",
287                   c, val, _reg[c]));
288       break;
289     case 2:
290       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr2",
291                   "wr2: [%d] %02xh -> %d\n new interrupt vector: %02x\n",
292                   c, val, _reg[c], val));
293       break;
294     case 3:
295       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr3",
296                   "wr3: [%d] %02xh -> %d\n",
297                   c, val, _reg[c]));
298       break;
299     case 4:
300       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr4",
301                   "wr4: [%d] %02xh -> %d\n",
302                   c, val, _reg[c]));
303       break;
304     case 5:
305       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr5",
306                   "wr5: [%d] %02xh -> %d\n",
307                   c, val, _reg[c]));
308       break;
309     case 6:
310       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr6",
311                   "wr6: [%d] %02xh -> %d\n",
312                   c, val, _reg[c]));
313       break;
314     case 7:
315       DBG(1, form("KCemu/ModuleV24/out_reg/reg/wr7",
316                   "wr7: [%d] %02xh -> %d\n",
317                   c, val, _reg[c]));
318       break;
319     }
320   if (_reg[c] == 0)
321     _reg[c] = val & 7;
322   else
323     _reg[c] = 0;
324 }
325 
326 ModuleInterface *
clone(void)327 ModuleV24::clone(void)
328 {
329   return new ModuleV24(*this);
330 }
331 
332 void
reset(bool power_on)333 ModuleV24::reset(bool power_on)
334 {
335   _irq_active[A] = 0;
336   _irq_active[B] = 0;
337 }
338 
339 void
reti(void)340 ModuleV24::reti(void)
341 {
342   _irq_active[B] = 0;
343   _irq_pending[B] = 0;
344 
345   if (!_in_buf_ptr)
346     return;
347 
348 //  if (z80->triggerIrq(_reg_wr[B][2]))
349 //    {
350 //      _irq_active[B] = 1;
351 //      _data_in[B] = *_in_buf_ptr++;
352 //      if (*_in_buf_ptr == 0) _in_buf_ptr = 0;
353 //      z80->handleIrq(_reg_wr[B][2]);
354 //      return;
355 //    }
356 //  else
357 //    _irq_pending[B] = 1;
358 }
359 
360 void
push_data(char * buf,int len)361 ModuleV24::push_data(char *buf, int len)
362 {
363   if (_in_buf_ptr)
364     {
365       cerr << "data ignored! ***" << endl;
366       return;
367     }
368 
369   if (_irq_active[B])
370     {
371       cout << "irq still active!" << endl;
372       return;
373     }
374 
375   strcpy((char *)_in_buf, buf);
376   _in_buf_ptr = _in_buf;
377 
378 //  if (z80->triggerIrq(_reg_wr[B][2]))
379 //    {
380 //      _irq_active[B] = 1;
381 //      _data_in[B] = *_in_buf_ptr++;
382 //      if (*_in_buf_ptr == 0) _in_buf_ptr = 0;
383 //      z80->handleIrq(_reg_wr[B][2]);
384 //      return;
385 //    }
386 //  else
387 //    _irq_pending[B] = 1;
388 }
389 
390 void
signal_handler_IO_read(int status)391 ModuleV24::signal_handler_IO_read(int status)
392 {
393   int a;
394 
395   static char buf[INBUF_LEN];
396 
397   DBG(1, form("KCemu/ModuleV24/signal",
398               "signal_handler_IO_read\n"));
399   a = read(self->_fd_in[B], buf, INBUF_LEN - 1);
400   buf[a] = '\0';
401   DBG(2, form("KCemu/ModuleV24/signal/data",
402               "got %d bytes: '%s'\n",
403               a, buf));
404   self->push_data(&buf[0], a);
405 }
406 
407 void
signal_handler_IO_recv(int status)408 ModuleV24::signal_handler_IO_recv(int status)
409 {
410   int a;
411 
412   static char buf[INBUF_LEN];
413 
414   DBG(1, form("KCemu/ModuleV24/signal",
415               "signal_handler_IO_recv\n"));
416   a = recv(self->_fd_in[B], buf, INBUF_LEN, 0);
417   buf[a] = '\0';
418   DBG(2, form("KCemu/ModuleV24/signal/data",
419               "got %d bytes: '%s'\n",
420               a, buf));
421   self->push_data(&buf[0], a);
422 }
423 
424 void
set_signal_handler(int fd,void (* sig_func)(int))425 ModuleV24::set_signal_handler(int fd, void (*sig_func)(int))
426 {
427   struct sigaction saio;
428 
429   DBG(1, form("KCemu/ModuleV24/signal",
430               "setting signal handler for fd = %d\n", fd));
431 
432   memset(&saio, 0, sizeof(saio));
433 
434   if (fcntl(fd, F_SETOWN, getpid()) < 0)
435     cerr << "can't set owner on filedescriptor " << fd << " to " << getpid() << endl;
436   if (fcntl(fd, F_SETFL, FASYNC) < 0)
437     cerr << "can't set FASYNC flag on filedescriptor " << fd << endl;
438 
439   self = this;
440   saio.sa_handler = sig_func;
441   sigemptyset(&saio.sa_mask);
442   saio.sa_flags = 0;
443 
444   sigaction(SIGIO, &saio, (struct sigaction *)NULL);
445 }
446 
447 bool
open_device_serial(int dev,const char * dev_name)448 ModuleV24::open_device_serial(int dev, const char *dev_name)
449 {
450   int baudrate = Preferences::instance()->get_int_value("v24_baudrate", 2400);
451 
452   _fd_in[dev] = open(dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
453   if (_fd_in[dev] == 0)
454     return false;
455   _fd_out[dev] = _fd_in[dev];
456 
457   set_signal_handler(_fd_in[dev], signal_handler_IO_read);
458 
459   tcgetattr(_fd_in[dev], &_tio_old[dev]);
460   bzero(&_tio_new[dev], sizeof(_tio_new[dev]));
461 
462   /*
463    *  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
464    *  CRTSCTS : output hardware flow control (only used if the cable has
465    *            all necessary lines. See sect. 7 of Serial-HOWTO)
466    *  CS8     : 8n1 (8bit,no parity,1 stopbit)
467    *  CLOCAL  : local connection, no modem contol
468    *  CREAD   : enable receiving characters
469    */
470   _tio_new[dev].c_cflag = CRTSCTS | CS8 | CLOCAL | CREAD;
471   switch (baudrate)
472     {
473     case 50:    _tio_new[dev].c_cflag |= B50   ; break;
474     case 75:    _tio_new[dev].c_cflag |= B75   ; break;
475     case 110:   _tio_new[dev].c_cflag |= B110  ; break;
476     case 134:   _tio_new[dev].c_cflag |= B134  ; break;
477     case 150:   _tio_new[dev].c_cflag |= B150  ; break;
478     case 200:   _tio_new[dev].c_cflag |= B200  ; break;
479     case 300:   _tio_new[dev].c_cflag |= B300  ; break;
480     case 600:   _tio_new[dev].c_cflag |= B600  ; break;
481     case 1200:  _tio_new[dev].c_cflag |= B1200 ; break;
482     case 1800:  _tio_new[dev].c_cflag |= B1800 ; break;
483     case 2400:  _tio_new[dev].c_cflag |= B2400 ; break;
484     case 4800:  _tio_new[dev].c_cflag |= B4800 ; break;
485     case 9600:  _tio_new[dev].c_cflag |= B9600 ; break;
486     case 19200: _tio_new[dev].c_cflag |= B19200; break;
487     case 38400: _tio_new[dev].c_cflag |= B38400; break;
488     default:
489       baudrate = 9600;
490       _tio_new[dev].c_cflag |= B9600 ;
491       break;
492     }
493 
494   DBG(1, form("KCemu/ModuleV24/open",
495               "ModuleV24::open_device_serial(): device = '%s', baudrate = %d\n",
496               dev_name, baudrate));
497 
498 
499   /*
500    *  IGNPAR  : ignore bytes with parity errors
501    *  ICRNL   : map CR to NL (otherwise a CR input on the other computer
502    *            will not terminate input)
503    *  otherwise make device raw (no other input processing)
504    */
505   _tio_new[dev].c_iflag = IGNPAR | ICRNL;
506   _tio_new[dev].c_iflag = IGNPAR;
507 
508   /*
509    *  Raw output.
510    */
511   _tio_new[dev].c_oflag = 0;
512 
513   /*
514    *  ICANON  : enable canonical input
515    *  disable all echo functionality, and don't send signals to calling program
516    */
517   _tio_new[dev].c_lflag = ICANON;
518   _tio_new[dev].c_lflag = 0;
519 
520   _tio_new[dev].c_cc[VMIN]=1;
521   _tio_new[dev].c_cc[VTIME]=0;
522 
523   tcflush(_fd_in[dev], TCIFLUSH);
524   tcsetattr(_fd_in[dev], TCSANOW, &_tio_new[dev]);
525 
526   return true;
527 }
528 
529 void
fifo_server(int fd)530 ModuleV24::fifo_server(int fd)
531 {
532   int ret, len, fd_read, fd_read_w, fd_write, fd_write_r;
533   struct pollfd pollfds[1];
534   char buf[1025];
535 
536   DBG(1, form("KCemu/ModuleV24/server",
537               "ModuleV24::fifo_server() fd = %d\n",
538               fd));
539 
540   if (unlink("/tmp/KCemu-FIFO-in") != 0)
541     perror("unlink");
542   if (unlink("/tmp/KCemu-FIFO-out") != 0)
543     perror("unlink");
544 
545   if (mkfifo("/tmp/KCemu-FIFO-in", 0600) != 0)
546     {
547       perror("can't create FIFO");
548       exit(1);
549     }
550   if (mkfifo("/tmp/KCemu-FIFO-out", 0600) != 0)
551     {
552       perror("can't create FIFO");
553       exit(1);
554     }
555 
556   fd_read = open("/tmp/KCemu-FIFO-in", O_RDONLY | O_NONBLOCK);
557   if (fd_read < 0)
558     {
559       perror("open (read)");
560       exit(1);
561     }
562   /*
563    *  open the fifo for writing too to keep it open if the
564    *  external writer closes it's side of the fifo
565    */
566   fd_read_w = open("/tmp/KCemu-FIFO-in", O_WRONLY | O_NONBLOCK);
567 
568   fd_write_r = open("/tmp/KCemu-FIFO-out", O_RDONLY | O_NONBLOCK);
569   fd_write = open("/tmp/KCemu-FIFO-out", O_WRONLY | O_NONBLOCK);
570   if (fd_write < 0)
571     {
572       perror("open (write)");
573       exit(1);
574     }
575 
576   while (242)
577     {
578       pollfds[0].fd = fd_read;
579       pollfds[0].events = POLLIN;
580       pollfds[0].revents = 0;
581       pollfds[1].fd = fd;
582       pollfds[1].events = POLLIN;
583       pollfds[1].revents = 0;
584       if ((ret = poll(pollfds, 2, -1)) < 0)
585         exit(2);
586 
587       if ((pollfds[0].revents & POLLIN) == POLLIN)
588         {
589           len = read(fd_read, buf, 1024);
590           if (len == 0)
591             {
592               cerr << "ModuleV24::fifo_server(): read error" << endl;
593               exit(1);
594             }
595           send(fd, buf, len, 0);
596         }
597       if ((pollfds[1].revents & POLLIN) == POLLIN)
598         {
599           len = read(fd, buf, 1024);
600           write(fd_write, buf, len);
601         }
602     }
603 }
604 
605 void
socket_server(int fd)606 ModuleV24::socket_server(int fd)
607 {
608   int s, c, len;
609   char buf[1024];
610   unsigned int cli_addr_len;
611   struct sockaddr_un unix_addr, cli_addr;
612 
613   _socket_name = tempnam("/tmp", "KCemu");
614   if (_socket_name == NULL)
615     exit(1);
616   _socket_name = strdup("/tmp/KCemu-in");
617 
618   s = socket(AF_UNIX, SOCK_STREAM, 0);
619   if (s < 0)
620     exit(2);
621 
622   bzero((char *)&unix_addr, sizeof(unix_addr));
623   unix_addr.sun_family = AF_UNIX;
624   strcpy(unix_addr.sun_path, _socket_name);
625   len = strlen(unix_addr.sun_path) + sizeof(unix_addr.sun_family);
626 
627   unlink(_socket_name);
628   if (bind(s, (struct sockaddr *)&unix_addr, len) != 0)
629     exit(3);
630 
631   if (listen(s, 5) != 0)
632     exit(4);
633 
634   while (242)
635     {
636       if ((c = accept(s, (struct sockaddr *)&cli_addr, &cli_addr_len)) < 0)
637         continue;
638 
639       while (242)
640         {
641           len = recv(c, buf, 1024, 0);
642           if (len == 0)
643             break;
644           send(fd, buf, len, 0);
645         }
646     }
647 }
648 
649 bool
open_device_socket_or_fifo(io_type_t io_type)650 ModuleV24::open_device_socket_or_fifo(io_type_t io_type)
651 {
652   int fd[2];
653 
654   /*
655    *  we use a pair of sockets for to communicate with the
656    *  child process, fd[0] is used by the server, fd[1] by
657    *  the child process
658    */
659   if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0)
660     return false;
661 
662   _pid = fork();
663 
664   if (_pid < 0)
665     return false;
666 
667   if (_pid > 0)
668     {
669       _fd_in[A] = fd[0];
670       _fd_out[A] = fd[0];
671       set_signal_handler(_fd_in[A], signal_handler_IO_recv);
672       return true;
673     }
674 
675   if (io_type == IO_FIFO)
676     fifo_server(fd[1]);
677   else
678     socket_server(fd[1]);
679   return true;
680 }
681 
682 void
open_device(void)683 ModuleV24::open_device(void)
684 {
685   bool ret;
686   char *s;
687   const char *dev_name;
688 
689   dev_name = Preferences::instance()->get_string_value("v24_device", "/dev/ttyS1");
690 
691   _io_type = IO_NONE;
692   if (strcmp(dev_name, "SOCKET") == 0)
693     {
694       _io_type = IO_SOCKET;
695       ret = open_device_socket_or_fifo(IO_SOCKET);
696     }
697   else if (strcmp(dev_name, "FIFO") == 0)
698     {
699       _io_type = IO_FIFO;
700       ret = open_device_socket_or_fifo(IO_FIFO);
701     }
702   else
703     {
704       _io_type = IO_FIFO;
705       ret = open_device_serial(B, dev_name);
706     }
707 
708   if (!ret)
709     return;
710 
711   z80->register_ic(this);
712 
713   s = "\r\nKCemu v" KCEMU_VERSION " - V24 module active\r\n\r\n";
714   write(_fd_out[B], s, strlen(s));
715 }
716 
717 void
close_device(void)718 ModuleV24::close_device(void)
719 {
720   if (_socket_name != 0)
721     {
722       unlink(_socket_name);
723       free(_socket_name);
724     }
725 
726   if (_fd_in[A] != 0)
727     {
728       tcsetattr(_fd_in[A] , TCSANOW, &_tio_old[A]);
729       close(_fd_in[A]);
730       close(_fd_out[A]);
731     }
732 
733   z80->unregister_ic(this);
734 }
735 
736 void
m_out(word_t addr,byte_t val)737 ModuleV24::m_out(word_t addr, byte_t val)
738 {
739 }
740