summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hw/char/terminal3270.c37
1 files changed, 32 insertions, 5 deletions
diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c
index 450608e3c3..58bcc4be15 100644
--- a/hw/char/terminal3270.c
+++ b/hw/char/terminal3270.c
@@ -32,6 +32,7 @@ typedef struct Terminal3270 {
     int in_len;
     int out_len;
     bool handshake_done;
+    guint timer_tag;
 } Terminal3270;
 
 #define TYPE_TERMINAL_3270 "x-terminal3270"
@@ -60,6 +61,19 @@ static void TN3270_handshake_done(Terminal3270 *t)
 }
 
 /*
+ * Called when the interval is timeout to detect
+ * if the client is still alive by Timing Mark.
+ */
+static gboolean send_timing_mark_cb(gpointer opaque)
+{
+    Terminal3270 *t = opaque;
+    const uint8_t timing[] = {0xff, 0xfd, 0x06};
+
+    qemu_chr_fe_write_all(&t->chr, timing, sizeof(timing));
+    return true;
+}
+
+/*
  * Receive inbound data from socket.
  * For data given to guest, drop the data boundary IAC, IAC_EOR.
  * TODO:
@@ -77,6 +91,12 @@ static void terminal_read(void *opaque, const uint8_t *buf, int size)
 
     assert(size <= (INPUT_BUFFER_SIZE - t->in_len));
 
+    if (t->timer_tag) {
+        g_source_remove(t->timer_tag);
+        t->timer_tag = 0;
+    }
+    t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t);
+
     memcpy(&t->inv[t->in_len], buf, size);
     t->in_len += size;
     if (t->in_len < 2) {
@@ -127,6 +147,10 @@ static void chr_event(void *opaque, int event)
     t->in_len = 0;
     t->out_len = 0;
     t->handshake_done = false;
+    if (t->timer_tag) {
+        g_source_remove(t->timer_tag);
+        t->timer_tag = 0;
+    }
 
     switch (event) {
     case CHR_EVENT_OPENED:
@@ -135,6 +159,7 @@ static void chr_event(void *opaque, int event)
          * char-socket.c. Once qemu receives the terminal-type of the
          * client, mark handshake done and trigger everything rolling again.
          */
+        t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t);
         break;
     case CHR_EVENT_CLOSED:
         sch->curr_status.scsw.dstat = SCSW_DSTAT_DEVICE_END;
@@ -206,11 +231,13 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd,
     assert(count <= (OUTPUT_BUFFER_SIZE - 3) / 2);
 
     if (!t->handshake_done) {
-        /*
-         * Before having finished 3270 negotiation,
-         * sending outbound data is prohibited.
-         */
-        return 0;
+        if (!(t->outv[0] == IAC && t->outv[1] != IAC)) {
+            /*
+             * Before having finished 3270 negotiation,
+             * sending outbound data except protocol options is prohibited.
+             */
+            return 0;
+        }
     }
     if (!qemu_chr_fe_get_driver(&t->chr)) {
         /* We just say we consumed all data if there's no backend. */