--- bochs/iodev/serial.cc 2022-05-06 07:31:17.780238890 +0700 +++ bochs/iodev/serial.cc 2022-09-08 07:37:53.459246721 +0700 @@ -74,6 +74,8 @@ "socket-server", "pipe-client", "pipe-server", + "null-modem", + "virtual-modem", NULL }; @@ -222,7 +224,17 @@ break; case BX_SER_MODE_SOCKET_CLIENT: case BX_SER_MODE_SOCKET_SERVER: - if (BX_SER_THIS s[i].socket_id >= 0) closesocket(BX_SER_THIS s[i].socket_id); + case BX_SER_MODE_NULL_MODEM: + case BX_SER_MODE_VIRTUAL_MODEM: + if (BX_SER_THIS s[i].socket_id >= 0) { + BX_INFO(("com%d: closing connection", i+1)); + closesocket(BX_SER_THIS s[i].socket_id); + BX_SER_THIS s[i].modem_status.dcd = 0; + BX_SER_THIS s[i].modem_status.delta_dcd = 1; + BX_SER_THIS s[i].socket_id = BX_SER_THIS s[i].server_socket_id; + BX_SER_THIS s[i].ms_ipending = 1; + raise_interrupt(i, BX_SER_INT_MODSTAT); + } #ifdef BX_SER_WIN32 if (winsock_init) { WSACleanup(); @@ -254,6 +266,8 @@ char name[16], pname[20]; bx_list_c *base, *misc_rt = NULL, *menu = NULL; unsigned i, count = 0; + int res = 0; + int nonb = 0; BX_SER_THIS detect_mouse = 0; BX_SER_THIS mouse_port = -1; @@ -370,6 +384,40 @@ BX_SER_THIS s[i].baudrate = 115200; BX_SER_THIS s[i].databyte_usec = 87; + /* Initialize the virtual modem */ + BX_SER_THIS s[i].virtual_modem.at_cmd_buf_idx = 0; + BX_SER_THIS s[i].virtual_modem.at_cmd_buf[0] = 0; + BX_SER_THIS s[i].virtual_modem.last_ring_time = 0; + BX_SER_THIS s[i].virtual_modem.tx_buffer_idx = 0; + BX_SER_THIS s[i].virtual_modem.tx_buffer_length = 0; + BX_SER_THIS s[i].virtual_modem.tx_buffer_send_time = 0; + + /* Virtual modem AT toggles */ + BX_SER_THIS s[i].virtual_modem.echo = false; + /* We keep the modem off hook by default. + This will deny connections until the software is ready. */ + BX_SER_THIS s[i].virtual_modem.off_hook = true; + BX_SER_THIS s[i].virtual_modem.quiet = false; + BX_SER_THIS s[i].virtual_modem.verbose = true; + + /* Virtual modem "S" registers */ + BX_SER_THIS s[i].virtual_modem.auto_answer = 0; + BX_SER_THIS s[i].virtual_modem.rings_sent = 0; + BX_SER_THIS s[i].virtual_modem.esc_char = '+'; + BX_SER_THIS s[i].virtual_modem.cr_char = '\r'; + BX_SER_THIS s[i].virtual_modem.lf_char = '\n'; + BX_SER_THIS s[i].virtual_modem.bs_char = 8; + BX_SER_THIS s[i].virtual_modem.blind_dial_wait = 0; + BX_SER_THIS s[i].virtual_modem.dial_carrier_wait = 0; + BX_SER_THIS s[i].virtual_modem.comma_pause_time = 2; + BX_SER_THIS s[i].virtual_modem.carrier_detect_time = 0; + BX_SER_THIS s[i].virtual_modem.no_carrier_hangup_time = 14; + BX_SER_THIS s[i].virtual_modem.escape_guard_time = 50; + BX_SER_THIS s[i].virtual_modem.at_delay = 0; + BX_SER_THIS s[i].virtual_modem.delay_to_dtr = 5; + BX_SER_THIS s[i].virtual_modem.rts_to_cts_delay = 1; + BX_SER_THIS s[i].virtual_modem.inactivity_timer = 0; + for (unsigned addr = ports[i]; addr < (unsigned)(ports[i] + 8); addr++) { BX_DEBUG(("com%d initialize register for read/write: 0x%04x", i + 1, addr)); if (addr < (unsigned)(ports[i] + 7)) { @@ -448,14 +496,21 @@ BX_SER_THIS s[i].io_mode = BX_SER_MODE_MOUSE; BX_SER_THIS mouse_port = i; BX_SER_THIS mouse_type = SIM->get_param_enum(BXPN_MOUSE_TYPE)->get(); - } else if ((mode == BX_SER_MODE_SOCKET_CLIENT) || - (mode == BX_SER_MODE_SOCKET_SERVER)) { + } else if ( (mode == BX_SER_MODE_SOCKET_CLIENT) + || (mode == BX_SER_MODE_SOCKET_SERVER) + || (mode == BX_SER_MODE_NULL_MODEM) + || (mode == BX_SER_MODE_VIRTUAL_MODEM) + ) { struct sockaddr_in sin; struct hostent *hp; char host[BX_PATHNAME_LEN]; int port; SOCKET socket; - bool server = (mode == BX_SER_MODE_SOCKET_SERVER); + bool server = ( + (mode == BX_SER_MODE_SOCKET_SERVER) + || (mode == BX_SER_MODE_NULL_MODEM) + || (mode == BX_SER_MODE_VIRTUAL_MODEM) + ); #ifdef BX_SER_WIN32 if (!winsock_init) { @@ -510,16 +565,16 @@ BX_PANIC(("com%d: bind() or listen() failed (host:%s, port:%d)",i+1, host, port)); continue; } else { - fprintf(stderr,"com%d: waiting for client to connect (host:%s, port:%d)\n",i+1, host, port); - SOCKET client; - if ((client = ::accept(socket, NULL, 0)) < 0) { - BX_PANIC(("com%d: accept() failed (host:%s, port:%d)",i+1, host, port)); - continue; - } else { - fprintf(stderr,"client connected\n"); - closesocket(socket); - socket = client; + /* Set non blocking */ + nonb |= O_NONBLOCK; + if ((res = fcntl(socket, F_GETFL, 0)) == -1) { + BX_PANIC(("com%d: fcntl(socket, F_GETFL) failed\n", i+1)); + continue; + } else if (fcntl(socket, F_SETFL, res | nonb) == -1) { + BX_PANIC(("com%d: fcntl(socket, F_SETFL, nonb) failed. res: %d\n", i+1, (res | nonb))); + continue; } + BX_SER_THIS s[i].server_socket_id = socket; } } else if (::connect(socket, (sockaddr *) &sin, sizeof (sin)) < 0) { // client mode @@ -1228,6 +1283,22 @@ break; case BX_SER_MCR: /* MODEM control register */ + /* When DTR goes down, drop socket connection */ + if ( (new_b0 == 0) && ( + (BX_SER_THIS s[port].io_mode == BX_SER_MODE_NULL_MODEM) + || (BX_SER_THIS s[port].io_mode == BX_SER_MODE_VIRTUAL_MODEM) + )) { + BX_INFO(("com%d: DTR dropped", port+1)); + if (BX_SER_THIS s[port].modem_status.dcd == 1) { + BX_INFO(("com%d: DCD was up, bringing down and closing socket", port+1)); + closesocket(BX_SER_THIS s[port].socket_id); + BX_SER_THIS s[port].socket_id = BX_SER_THIS s[port].server_socket_id; + BX_SER_THIS s[port].modem_status.dcd = 0; + BX_SER_THIS s[port].modem_status.delta_dcd = 1; + BX_SER_THIS s[port].ms_ipending = 1; + raise_interrupt(port, BX_SER_INT_MODSTAT); + } + } if ((BX_SER_THIS s[port].io_mode == BX_SER_MODE_MOUSE) && ((BX_SER_THIS s[port].line_cntl.wordlen_sel == 2) || (BX_SER_THIS s[port].line_cntl.wordlen_sel == 3))) { @@ -1347,7 +1418,7 @@ BX_SER_THIS s[port].modem_status.cts = 1; BX_SER_THIS s[port].modem_status.dsr = 1; BX_SER_THIS s[port].modem_status.ri = 0; - BX_SER_THIS s[port].modem_status.dcd = 0; + /* BX_SER_THIS s[port].modem_status.dcd = 0; */ } } break; @@ -1378,7 +1449,7 @@ if (BX_SER_THIS s[port].fifo_cntl.enable) { if (BX_SER_THIS s[port].rx_fifo_end == 16) { if (!BX_SER_THIS s[port].modem_cntl.local_loopback) { - BX_ERROR(("com%d: receive FIFO overflow", port+1)); + BX_ERROR(("com%d: receive FIFO overflow, char: %d", port+1, data)); } BX_SER_THIS s[port].line_status.overrun_error = 1; raise_interrupt(port, BX_SER_INT_RXLSTAT); @@ -1412,10 +1483,11 @@ BX_ERROR(("com%d: overrun error", port+1)); BX_SER_THIS s[port].line_status.overrun_error = 1; raise_interrupt(port, BX_SER_INT_RXLSTAT); + } else { + BX_SER_THIS s[port].rxbuffer = data; + BX_SER_THIS s[port].line_status.rxdata_ready = 1; + raise_interrupt(port, BX_SER_INT_RXDATA); } - BX_SER_THIS s[port].rxbuffer = data; - BX_SER_THIS s[port].line_status.rxdata_ready = 1; - raise_interrupt(port, BX_SER_INT_RXDATA); } } @@ -1429,6 +1501,16 @@ void bx_serial_c::tx_timer(void) { bool gen_int = 0; + bool at_error = 0; + bool at_silent = 0; + char *s; + char *v; + timespec thetime; + Bit64u mstime; + + clock_gettime(CLOCK_MONOTONIC, &thetime); + mstime = (Bit64u)(thetime.tv_sec*1000)+(Bit64u)(thetime.tv_nsec/1000000); + Bit8u port = (Bit8u)bx_pc_system.triggeredTimerParam(); switch (BX_SER_THIS s[port].io_mode) { @@ -1465,6 +1547,198 @@ case BX_SER_MODE_MOUSE: BX_INFO(("com%d: write to mouse ignored: 0x%02x", port+1, BX_SER_THIS s[port].tsrbuffer)); break; + case BX_SER_MODE_VIRTUAL_MODEM: + if (BX_SER_THIS s[port].virtual_modem.echo) { + rx_fifo_enq(port, BX_SER_THIS s[port].tsrbuffer); + } + if (!BX_SER_THIS s[port].modem_status.dcd) { + /* Handle AT commands sent to the virtual modem */ + /* This is messy AF, probably needs its own function */ + if (BX_SER_THIS s[port].tsrbuffer == BX_SER_THIS s[port].virtual_modem.cr_char) { + if ( + (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[0] != 'A') + && (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[1] != 'T') + ) { + BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[0] = 0; + break; + } + at_error = 0; /* Respond OK by default */ + at_silent = 0; + BX_INFO(("com%d: AT command: %s", + port+1, + BX_SER_THIS s[port].virtual_modem.at_cmd_buf + )); + for (int i = 2 ; i < BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx; i++) { + switch(BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i]) { + case ' ': /* Spaces are ignored */ + break; + case 'A': + if (BX_SER_THIS s[port].virtual_modem.rings_sent) { + BX_INFO(("com%d: Got ATA from the VM, holding to simulate modem connect time.", port+1)); + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "CONNECT 14400", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "14", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime + (BX_SER_THIS s[port].virtual_modem.carrier_detect_time * 100); + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[0] = 0; + BX_SER_THIS s[port].virtual_modem.off_hook = 1; + BX_SER_THIS s[port].virtual_modem.rings_sent = 0; + BX_SER_THIS s[port].virtual_modem.last_ring_time = 0; + BX_SER_THIS s[port].modem_status.dcd = 1; + BX_SER_THIS s[port].modem_status.delta_dcd = 1; + BX_SER_THIS s[port].modem_status.ri = 0; + BX_SER_THIS s[port].modem_status.ri_trailedge = 0; + BX_SER_THIS s[port].ms_ipending = 1; + raise_interrupt(port, BX_SER_INT_MODSTAT); + at_silent = 1; + } + while (isdigit(BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1])) { + i++; + } + break; + case 'D': + /* Outbound dialing not supported */ + BX_INFO(("com%d: Sending ERROR in reply to ATD command.", port+1)); + at_error = 1; + i++; + break; + case 'E': + if ( (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1] != '0') + && (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1] != '1') + ) { + break; + } + i++; + BX_SER_THIS s[port].virtual_modem.echo = BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i] - '0'; + break; + case 'H': + if ( (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1] != '0') + && (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1] != '1') + ) { + BX_SER_THIS s[port].virtual_modem.off_hook = 0; + BX_INFO(("com%d: Virtual modem is now on the hook.",port+1)); + break; + } + i++; + BX_SER_THIS s[port].virtual_modem.off_hook = BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i] - '0'; + if (!BX_SER_THIS s[port].virtual_modem.off_hook) { + BX_INFO(("com%d: Virtual modem is now on the hook.",port+1)); + } + break; + case 'S': /* S registers */ + i++; /* first digit of register number */ + s = &BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i]; + while (isdigit(BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1])) { + i++; + } + i++; /* equals sign */ + i++; /* first digit of value */ + v = &BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i]; + while (isdigit(BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1])) { + i++; + } + switch(atoi(s)) { + case 0: + BX_SER_THIS s[port].virtual_modem.auto_answer = atoi(v); + break; + case 9: + BX_SER_THIS s[port].virtual_modem.carrier_detect_time = atoi(v); + break; + case 18: + BX_SER_THIS s[port].virtual_modem.at_delay = atoi(v); + break; + default: + break; + } + break; + case 'V': + if ( (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1] != '0') + && (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1] != '1') + ) { + break; + } + i++; + BX_SER_THIS s[port].virtual_modem.verbose = BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i] - '0'; + break; + default: /* Covers ATZ, et al */ + if (BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i] == '&') { + i++; + } + while (isdigit(BX_SER_THIS s[port].virtual_modem.at_cmd_buf[i+1])) { + i++; + } + break; + } + } + if (!at_silent) { + if (at_error) { + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "ERROR", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "1", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + } else { + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "OK", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "0", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + } + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime + (BX_SER_THIS s[port].virtual_modem.at_delay * 100); + } + BX_SER_THIS s[port].modem_status.delta_cts = 1; + BX_SER_THIS s[port].modem_status.delta_dsr = 1; + BX_SER_THIS s[port].modem_status.cts = 1; + BX_SER_THIS s[port].modem_status.dsr = 1; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[0] = 0; + break; + } + /* Add to buffer and move on */ + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx] = BX_SER_THIS s[port].tsrbuffer; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx++; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx] = 0; + /* AT command at 40 chars? flush everything */ + if (BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx > 39) { + BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[0] = 0; + } + break; + } /* Active connection, purposefully fall through here */ + case BX_SER_MODE_NULL_MODEM: + if (BX_SER_THIS s[port].modem_status.dcd != 1) + break; case BX_SER_MODE_SOCKET_CLIENT: case BX_SER_MODE_SOCKET_SERVER: if (BX_SER_THIS s[port].socket_id >= 0) { @@ -1523,6 +1797,12 @@ bool data_ready = 0; int db_usec = BX_SER_THIS s[port].databyte_usec; unsigned char chbuf = 0; + int newfd; + timespec thetime; + Bit64u mstime; + + clock_gettime(CLOCK_MONOTONIC, &thetime); + mstime = (Bit64u)(thetime.tv_sec*1000)+(Bit64u)(thetime.tv_nsec/1000000); if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_TERM) { #if BX_HAVE_SELECT && defined(SERIAL_ENABLE) @@ -1541,6 +1821,298 @@ if ((BX_SER_THIS s[port].line_status.rxdata_ready == 0) || (BX_SER_THIS s[port].fifo_cntl.enable)) { switch (BX_SER_THIS s[port].io_mode) { + case BX_SER_MODE_NULL_MODEM: + case BX_SER_MODE_VIRTUAL_MODEM: + if (BX_SER_THIS s[port].modem_status.dcd == 1) { + if ( BX_SER_THIS s[port].virtual_modem.tx_buffer[0] + && (mstime > BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time) + ) { + if (BX_SER_THIS s[port].fifo_cntl.enable) { + BX_INFO(("com%d: Sending buffered data that was held via FIFO.", port+1)); + for (int i=0;BX_SER_THIS s[port].virtual_modem.tx_buffer[i] != 0;i++) { + rx_fifo_enq(port, BX_SER_THIS s[port].virtual_modem.tx_buffer[i]); + } + } else { /* no FIFO */ + BX_INFO(("com%d: Sending buffered data that was held without FIFO.", port+1)); + if (BX_SER_THIS s[port].line_status.rxdata_ready == 1) { + BX_INFO(("com%d: Virtual modem data ready. UART is not.", port+1)); + break; + } + BX_SER_THIS s[port].rxbuffer = + BX_SER_THIS s[port].virtual_modem.tx_buffer[ + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx + ]; + BX_SER_THIS s[port].line_status.rxdata_ready = 1; + raise_interrupt(port, BX_SER_INT_RXDATA); + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx++; + if (BX_SER_THIS s[port].virtual_modem.tx_buffer[ + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx] != 0) { + break; + } + } + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx = 0; + BX_SER_THIS s[port].virtual_modem.tx_buffer[0] = 0; + break; + } + if ((newfd = accept(BX_SER_THIS s[port].server_socket_id, NULL, NULL)) > 0) { + write(newfd, + "Sorry, the virtual modem is busy with another call.\r\n", + 47 + ); + closesocket(newfd); + break; + } + if (BX_SER_THIS s[port].line_status.rxdata_ready == 0) { + tval.tv_sec = 0; + tval.tv_usec = 0; + FD_ZERO(&fds); + SOCKET socketid = BX_SER_THIS s[port].socket_id; + if (socketid >= 0) { + FD_SET(socketid, &fds); + if (recv(socketid, (char*) &chbuf, 1, MSG_PEEK | MSG_DONTWAIT) == 0) { + BX_INFO(("com%d: connection reset by peer", port+1)); + closesocket(BX_SER_THIS s[port].socket_id); + BX_SER_THIS s[port].modem_status.dcd = 0; + BX_SER_THIS s[port].modem_status.delta_dcd = 1; + BX_SER_THIS s[port].socket_id = BX_SER_THIS s[port].server_socket_id; + BX_SER_THIS s[port].virtual_modem.last_ring_time = 0; + BX_SER_THIS s[port].virtual_modem.rings_sent = 0; + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime; + if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_VIRTUAL_MODEM) { + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "NO CARRIER", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "3", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + } + BX_SER_THIS s[port].ms_ipending = 1; + /* BX_SER_THIS s[port].int_enable.modstat_enable = 1; */ + raise_interrupt(port, BX_SER_INT_MODSTAT); + break; + } + + if (select((int)(socketid+1), &fds, NULL, NULL, &tval) == 1) { + ssize_t bytes = (ssize_t) + ::recv(socketid, (char*) &chbuf, 1, 0); + if (bytes > 0) { + BX_DEBUG(("com%d: read byte [0x%02x]", port+1, chbuf)); + data_ready = 1; + } + } + } + } + break; + } + + if ( BX_SER_THIS s[port].virtual_modem.tx_buffer[0] + && (mstime > BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time) + ) { + if (BX_SER_THIS s[port].fifo_cntl.enable) { + for (int i=0; BX_SER_THIS s[port].virtual_modem.tx_buffer[i] != 0 ; i++) { + rx_fifo_enq(port, BX_SER_THIS s[port].virtual_modem.tx_buffer[i]); + } + } else { /* no FIFO */ + if (BX_SER_THIS s[port].line_status.rxdata_ready == 1) { + BX_INFO(("com%d: Virtual modem data ready. UART is not.", port+1)); + break; + } + BX_SER_THIS s[port].rxbuffer = + BX_SER_THIS s[port].virtual_modem.tx_buffer[ + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx + ]; + BX_SER_THIS s[port].line_status.rxdata_ready = 1; + raise_interrupt(port, BX_SER_INT_RXDATA); + if (BX_SER_THIS s[port].virtual_modem.tx_buffer[ + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx++] != 0) { + break; + } + } + BX_INFO(("Resetting virtual modem's TX buffer")); + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx = 0; + BX_SER_THIS s[port].virtual_modem.tx_buffer[0] = 0; + break; + } + if ( BX_SER_THIS s[port].virtual_modem.last_ring_time + && recv(BX_SER_THIS s[port].socket_id, (char*) &chbuf, 1, MSG_PEEK | MSG_DONTWAIT) == 0) { + BX_INFO(("com%d: While RING, connection reset by peer", port+1)); + closesocket(BX_SER_THIS s[port].socket_id); +/* if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "NO CARRIER", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "3", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } */ + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime; + BX_SER_THIS s[port].modem_status.ri = 0; + BX_SER_THIS s[port].modem_status.ri_trailedge = 0; + BX_SER_THIS s[port].virtual_modem.last_ring_time = 0; + BX_SER_THIS s[port].virtual_modem.rings_sent = 0; + BX_SER_THIS s[port].virtual_modem.off_hook = 0; + BX_SER_THIS s[port].ms_ipending = 1; +/* BX_SER_THIS s[port].int_enable.modstat_enable = 1; */ + raise_interrupt(port, BX_SER_INT_MODSTAT); + break; + } + if (BX_SER_THIS s[port].virtual_modem.last_ring_time) { + if (BX_SER_THIS s[port].virtual_modem.rings_sent >= 5) { + BX_INFO(("com%d: Timeout after five RINGs sent. Disconnecting.", port+1)); +/* if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "NO CARRIER", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "3", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } */ + closesocket(BX_SER_THIS s[port].socket_id); + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime; + BX_SER_THIS s[port].modem_status.ri = 0; + BX_SER_THIS s[port].modem_status.ri_trailedge = 0; + BX_SER_THIS s[port].virtual_modem.last_ring_time = 0; + BX_SER_THIS s[port].virtual_modem.rings_sent = 0; + BX_SER_THIS s[port].virtual_modem.off_hook = 0; + BX_SER_THIS s[port].ms_ipending = 1; +/* BX_SER_THIS s[port].int_enable.modstat_enable = 1; */ + raise_interrupt(port, BX_SER_INT_MODSTAT); + break; + } + if ( (BX_SER_THIS s[port].virtual_modem.last_ring_time + 3000) < mstime) { + BX_INFO(("com%d: Three seconds since last RING, sending another.", port+1)); + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "RING", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "2", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime; + BX_SER_THIS s[port].modem_status.ri_trailedge = 1; + BX_SER_THIS s[port].modem_status.ri = 1; + BX_SER_THIS s[port].virtual_modem.last_ring_time = mstime; + BX_SER_THIS s[port].virtual_modem.rings_sent++; + raise_interrupt(port, BX_SER_INT_MODSTAT); + break; + } + } + if ((newfd = accept(BX_SER_THIS s[port].server_socket_id, NULL, NULL)) > 0) { + if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_VIRTUAL_MODEM) { + if (BX_SER_THIS s[port].virtual_modem.off_hook) { + write(newfd, + "The virtual modem is off the hook and isn't accepting calls.\r\n", + 62 + ); + closesocket(newfd); + break; + } + if (BX_SER_THIS s[port].virtual_modem.rings_sent != 0) { + write(newfd, + "The virtual modem is already ringing from another caller.\r\n", + 59 + ); + closesocket(newfd); + break; + } + write(newfd, "The virtual modem is ringing, please wait...\r\n", 47); + } + if (BX_SER_THIS s[port].modem_status.dcd == 1) { + write(newfd, "Sorry, this line is busy with another call.\r\n", 46); + closesocket(newfd); + break; + } + BX_SER_THIS s[port].socket_id = newfd; + if ( (BX_SER_THIS s[port].io_mode == BX_SER_MODE_NULL_MODEM) + || BX_SER_THIS s[port].virtual_modem.auto_answer + ) { + if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_VIRTUAL_MODEM) { + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "CONNECT 14400", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "14", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + } + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime; + BX_SER_THIS s[port].virtual_modem.tx_buffer_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf_idx = 0; + BX_SER_THIS s[port].virtual_modem.at_cmd_buf[0] = 0; + BX_SER_THIS s[port].virtual_modem.off_hook = 1; + BX_SER_THIS s[port].virtual_modem.rings_sent = 0; + BX_SER_THIS s[port].virtual_modem.last_ring_time = 0; + BX_SER_THIS s[port].modem_status.dcd = 1; + BX_SER_THIS s[port].modem_status.delta_dcd = 1; + BX_SER_THIS s[port].modem_status.ri = 0; + BX_SER_THIS s[port].modem_status.ri_trailedge = 0; + BX_SER_THIS s[port].ms_ipending = 1; +/* BX_SER_THIS s[port].int_enable.modstat_enable = 1; */ + raise_interrupt(port, BX_SER_INT_MODSTAT); + break; + } + if (BX_SER_THIS s[port].virtual_modem.rings_sent == 0) { + if (BX_SER_THIS s[port].virtual_modem.verbose) { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c%c", + "RING", + BX_SER_THIS s[port].virtual_modem.cr_char, + BX_SER_THIS s[port].virtual_modem.lf_char + ); + } else { + sprintf(BX_SER_THIS s[port].virtual_modem.tx_buffer, + "%s%c", + "2", + BX_SER_THIS s[port].virtual_modem.cr_char + ); + } + BX_INFO(("com%d: accepted new TCP connection, sent RING", port+1)); + BX_SER_THIS s[port].virtual_modem.tx_buffer_send_time = mstime; + BX_SER_THIS s[port].virtual_modem.last_ring_time = mstime; + BX_SER_THIS s[port].virtual_modem.rings_sent = 1; + BX_SER_THIS s[port].virtual_modem.off_hook = 1; + BX_SER_THIS s[port].modem_status.ri_trailedge = 1; + BX_SER_THIS s[port].modem_status.ri = 1; + BX_SER_THIS s[port].ms_ipending = 1; +/* BX_SER_THIS s[port].int_enable.modstat_enable = 1; */ + raise_interrupt(port, BX_SER_INT_MODSTAT); + } + } + break; case BX_SER_MODE_SOCKET_CLIENT: case BX_SER_MODE_SOCKET_SERVER: #if BX_HAVE_SELECT && defined(SERIAL_ENABLE) --- bochs/iodev/serial.h 2022-05-06 07:31:21.132121231 +0700 +++ bochs/iodev/serial.h 2022-05-06 08:25:33.764280076 +0700 @@ -72,6 +72,9 @@ #define BX_SER_MODE_SOCKET_SERVER 6 #define BX_SER_MODE_PIPE_CLIENT 7 #define BX_SER_MODE_PIPE_SERVER 8 +#define BX_SER_MODE_NULL_MODEM 9 +#define BX_SER_MODE_VIRTUAL_MODEM 10 +#define BX_SER_MODE_SOCKET_SERVER_WAITING 11 enum { BX_SER_INT_IER, @@ -115,6 +118,7 @@ int io_mode; int tty_id; SOCKET socket_id; + SOCKET server_socket_id; FILE *output; bx_param_string_c *file; #ifdef BX_SER_WIN32 @@ -190,6 +194,42 @@ bool ri; /* RI input value */ bool dcd; /* DCD input value */ } modem_status; + /* Virtual Modem */ + struct { + /* Regular AT toggles */ + bool echo; /* ATE */ + bool off_hook; /* ATH - 0 = On hook, 1 = Off hook */ + bool quiet; /* ATQ */ + bool verbose; /* ATV - 0 = Numeric, 1 = English */ + + /* "S" Registers */ + Bit8u auto_answer; /* S0 - Number of rings, 0 = never */ + Bit8u rings_sent; /* S1 - Read-only, stores # of rings */ + Bit8u esc_char; /* S2 - ASCII value, default: 43 ("+") */ + Bit8u cr_char; /* S3 - ASCII value, default: 13 (CR) */ + Bit8u lf_char; /* S4 - ASCII value, default: 10 (LF) */ + Bit8u bs_char; /* S5 - ASCII value, default: 8 (BS) */ + Bit8u blind_dial_wait; /* S6 */ + Bit8u dial_carrier_wait; /* S7 */ + Bit8u comma_pause_time; /* S8 */ + Bit8u carrier_detect_time; /* S9 - Delay between ATA and CONNECT 1/10 sec */ + Bit8u no_carrier_hangup_time; /* S10 - Tenths of a second */ + Bit8u escape_guard_time; /* S12 - Fiftieths of a second */ + Bit8u at_delay; /* S18 - Delay between AT and OK 1/10 sec */ + Bit8u delay_to_dtr; /* S25 */ + Bit8u rts_to_cts_delay; /* S26 - Hundredths of a second */ + Bit8u inactivity_timer; /* S30 - Tenths of a second */ + + char tx_buffer[40]; /* Used in both command and data modes */ + Bit8u tx_buffer_idx; + Bit8u tx_buffer_length; + Bit64u tx_buffer_send_time; /* Used to delay buffer responses in tandem with S18 */ + + Bit64u last_ring_time; /* Used to space rings in a real-world fashion */ + + int at_cmd_buf_idx; + char at_cmd_buf[40]; + } virtual_modem; Bit8u scratch; /* Scratch Register (r/w) */ Bit8u tsrbuffer; /* transmit shift register (internal) */ --- bochs/gui/gui.cc 2021-06-07 18:30:08.022810000 +0700 +++ bochs/gui/gui.cc 2022-05-06 07:31:33.227696824 +0700 @@ -1020,7 +1020,7 @@ void bx_gui_c::show_ips(Bit32u ips_count) { #if BX_SHOW_IPS - BX_INFO(("ips = %3.3fM", ips_count / 1000000.0)); + BX_DEBUG(("ips = %3.3fM", ips_count / 1000000.0)); #endif }