[prev in list] [next in list] [prev in thread] [next in thread] 

List:       qemu-devel
Subject:    [Qemu-devel] [PATCH] 1st improvement of serial.c (Modemstatus etc.)
From:       Juergen Pfennig <info () j-pfennig ! de>
Date:       2006-01-16 14:19:12
Message-ID: 200601161519.12511.info () j-pfennig ! de
[Download RAW message or body]

Hello,
as I promised I will send QEMU patches even if I don't know about QEMU's patch
policy ... Here a more complex one:

serial.c (and vl.h):

(1) sending break
(2) outgoing modem state
(3) incomming modem state
(4) CHECK THIS: support to disable interrupts (a PC feature)

in vl.c (and vl.h):

(1) host serial implementation extended
(2) CHECK THIS: post-processing disabled
(3) the if/else cascade for -net was cleaned-up a little

This patch is sufficent for win2003 to detect and support a smartcard reader
like towitoko chipdrive micro.

The driver has still missing features like parity error detection, modem 
status change interrupts and 16650 support!

Yours Jürgen

["patch2_serial" (text/x-diff)]

Index: vl.c
===================================================================
RCS file: /home/Cvsroot/qemu/vl.c,v
retrieving revision 1.1.1.1
diff -B -b -U3 -r1.1.1.1 vl.c
--- vl.c	14 Jan 2006 13:19:59 -0000	1.1.1.1
+++ vl.c	15 Jan 2006 20:26:54 -0000
@@ -1468,9 +1468,11 @@
 
     tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
                           |INLCR|IGNCR|ICRNL|IXON);
-    tty.c_oflag |= OPOST;
+// TODO: OPOST clear or set ?
+//    tty.c_oflag |= OPOST;
+    tty.c_oflag  = 0;
     tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
-    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS);
+    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
     switch(data_bits) {
     default:
     case 8:
@@ -1486,6 +1488,11 @@
         tty.c_cflag |= CS5;
         break;
     }
+
+    if(stop_bits > 1)
+        tty.c_cflag |= CSTOPB;
+    tty.c_cflag |= CREAD | CLOCAL;
+
     switch(parity) {
     default:
     case 'N':
@@ -1516,10 +1523,57 @@
     case CHR_IOCTL_SERIAL_SET_BREAK:
         {
             int enable = *(int *)arg;
+#if defined(TIOCSBRK) && !defined(_WIN32)
+            // Linux should use TIOCSBRK and TIOCCBRK
+            if(ioctl(s->fd_in, enable ? TIOCSBRK : TIOCCBRK) == 0)
+                break;
+            // don't care about pending interrupt ...
+            if(errno == EINTR)
+                break;
+            perror("CHR_IOCTL_SERIAL_SET_BREAK fallback to tcsendbreak()");
+#endif
+            // here arg=1 is used and in the linux tty driver the code is:
+            //        send_break(tty, arg ? arg*100 : 250);
             if (enable)
                 tcsendbreak(s->fd_in, 1);
         }
         break;
+
+    case CHR_IOCTL_SERIAL_SET_MSTAT:
+#if defined(TIOCMBIS) && !defined(_WIN32)
+        {
+            int tiomsk = TIOCM_DTR | TIOCM_RTS;
+            int tiobit = 0;
+            if((*(int *)arg) & CHR_IOCTL_SERIAL_DTR) tiobit |= TIOCM_DTR;
+            if((*(int *)arg) & CHR_IOCTL_SERIAL_RTS) tiobit |= TIOCM_RTS;
+
+            if(tiobit != 0)
+                ioctl(s->fd_in, TIOCMBIS, &tiobit);
+            if(tiobit != tiomsk) {
+                tiobit = (~tiobit) & tiomsk;
+                ioctl(s->fd_in, TIOCMBIC, &tiobit);
+            }
+        }
+#endif
+        break;
+
+    case CHR_IOCTL_SERIAL_GET_MSTAT:
+        {
+            int tiores = -1;
+#if defined(TIOCMGET) && !defined(_WIN32)
+            int tiobit;
+            if( ioctl(s->fd_in, TIOCMGET, &tiobit) == 0 ) {
+                tiores = 0;
+                if(tiobit & TIOCM_CTS) tiores |= CHR_IOCTL_SERIAL_CTS;
+                if(tiobit & TIOCM_CAR) tiores |= CHR_IOCTL_SERIAL_DCD;
+                if(tiobit & TIOCM_RNG) tiores |= CHR_IOCTL_SERIAL_RI;
+                if(tiobit & TIOCM_DSR) tiores |= CHR_IOCTL_SERIAL_DSR;
+            }
+#endif
+            *(int *)arg = tiores;
+        }
+        break;
+
     default:
         return -ENOTSUP;
     }
@@ -2620,19 +2674,19 @@
         nd->vlan = vlan;
         nb_nics++;
         ret = 0;
-    } else
-    if (!strcmp(device, "none")) {
+    }
+    else if (!strcmp(device, "none")) {
         /* does nothing. It is needed to signal that no network cards
            are wanted */
         ret = 0;
-    } else
+    }
 #ifdef CONFIG_SLIRP
-    if (!strcmp(device, "user")) {
+    else if (!strcmp(device, "user")) {
         ret = net_slirp_init(vlan);
-    } else
+    }
 #endif
 #ifndef _WIN32
-    if (!strcmp(device, "tap")) {
+    else if (!strcmp(device, "tap")) {
         char ifname[64];
         char setup_script[1024];
         int fd;
@@ -2648,8 +2702,8 @@
             }
             ret = net_tap_init(vlan, ifname, setup_script);
         }
-    } else
-    if (!strcmp(device, "socket")) {
+    }
+    else if (!strcmp(device, "socket")) {
         if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
             int fd;
             fd = strtol(buf, NULL, 0);
@@ -2666,8 +2720,9 @@
             fprintf(stderr, "Unknown socket options: %s\n", p);
             return -1;
         }
-    } else
+    }
 #endif
+    else
     {
         fprintf(stderr, "Unknown network device: %s\n", device);
         return -1;
@@ -3655,7 +3710,7 @@
     return 0;
 }
 
-QEMUMachine *find_machine(const char *name)
+static QEMUMachine *find_machine(const char *name)
 {
     QEMUMachine *m;
 
@@ -3669,7 +3724,7 @@
 /***********************************************************/
 /* main execution loop */
 
-void gui_update(void *opaque)
+static void gui_update(void *opaque)
 {
     display_state.dpy_refresh(&display_state);
     qemu_mod_timer(gui_timer, GUI_REFRESH_INTERVAL + qemu_get_clock(rt_clock));
@@ -3886,6 +3941,8 @@
         /* real time timers */
         qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME], 
                         qemu_get_clock(rt_clock));
+
+        // TODO: add a timer to check serial lines for Modem-Status-Register changes
 }
 
 static CPUState *cur_cpu;
Index: vl.h
===================================================================
RCS file: /home/Cvsroot/qemu/vl.h,v
retrieving revision 1.1.1.1
diff -B -b -U3 -r1.1.1.1 vl.h
--- vl.h	14 Jan 2006 13:19:59 -0000	1.1.1.1
+++ vl.h	13 Jan 2006 13:43:32 -0000
@@ -219,8 +219,21 @@
     int stop_bits;
 } QEMUSerialSetParams;
 
+// turn BREAK on/off
 #define CHR_IOCTL_SERIAL_SET_BREAK    2
 
+// set modemstatus 1:=DTR 2:=RTS (like the two lower bits in MCR)
+#define CHR_IOCTL_SERIAL_SET_MSTAT    8
+   #define CHR_IOCTL_SERIAL_DTR    0x01
+   #define CHR_IOCTL_SERIAL_RTS    0x02
+
+// get modemstatus 0x80:=DCD 0x40:=RI 0x20:=DSR 0x10:=CTS (see MSR)
+#define CHR_IOCTL_SERIAL_GET_MSTAT    9
+   #define CHR_IOCTL_SERIAL_DCD    0x80
+   #define CHR_IOCTL_SERIAL_RI     0x40
+   #define CHR_IOCTL_SERIAL_DSR    0x20
+   #define CHR_IOCTL_SERIAL_CTS    0x10
+
 #define CHR_IOCTL_PP_READ_DATA        3
 #define CHR_IOCTL_PP_WRITE_DATA       4
 #define CHR_IOCTL_PP_READ_CONTROL     5
Index: hw/serial.c
===================================================================
RCS file: /home/Cvsroot/qemu/hw/serial.c,v
retrieving revision 1.1.1.1
diff -B -b -U3 -r1.1.1.1 serial.c
--- hw/serial.c	14 Jan 2006 13:19:59 -0000	1.1.1.1
+++ hw/serial.c	15 Jan 2006 18:28:38 -0000
@@ -23,7 +23,7 @@
  */
 #include "vl.h"
 
-//#define DEBUG_SERIAL
+#define DEBUG_SERIAL_
 
 #define UART_LCR_DLAB	0x80	/* Divisor latch access bit */
 
@@ -71,7 +71,7 @@
 #define UART_LSR_DR	0x01	/* Receiver data ready */
 
 struct SerialState {
-    uint8_t divider;
+    uint16_t divider;
     uint8_t rbr; /* receive register */
     uint8_t ier;
     uint8_t iir; /* read only */
@@ -101,7 +101,8 @@
     } else {
         s->iir = UART_IIR_NO_INT;
     }
-    if (s->iir != UART_IIR_NO_INT) {
+    // mcr OUT2 is used to enable interupts!
+    if (s->iir != UART_IIR_NO_INT && (s->mcr & 8)) {
         s->set_irq(s->irq_opaque, s->irq, 1);
     } else {
         s->set_irq(s->irq_opaque, s->irq, 0);
@@ -144,6 +145,7 @@
 {
     SerialState *s = opaque;
     unsigned char ch;
+    unsigned int idiv;
     
     addr &= 7;
 #ifdef DEBUG_SERIAL
@@ -153,13 +155,17 @@
     default:
     case 0:
         if (s->lcr & UART_LCR_DLAB) {
-            s->divider = (s->divider & 0xff00) | val;
+            idiv = (s->divider & 0xff00) | val;
+            if(idiv != s->divider) {
+                s->divider = idiv;
             serial_update_parameters(s);
+            }
         } else {
             s->thr_ipending = 0;
             s->lsr &= ~UART_LSR_THRE;
             serial_update_irq(s);
             ch = val;
+//          printf("serial: char %x '%c'\n", val, val >= 32 && val < 127 ? val : \
'?');  qemu_chr_write(s->chr, &ch, 1);
             s->thr_ipending = 1;
             s->lsr |= UART_LSR_THRE;
@@ -169,8 +175,11 @@
         break;
     case 1:
         if (s->lcr & UART_LCR_DLAB) {
-            s->divider = (s->divider & 0x00ff) | (val << 8);
+            idiv = (s->divider & 0x00ff) | (val << 8);
+            if(idiv != s->divider) {
+                s->divider = idiv;
             serial_update_parameters(s);
+            }
         } else {
             s->ier = val & 0x0f;
             if (s->lsr & UART_LSR_THRE) {
@@ -184,18 +193,29 @@
     case 3:
         {
             int break_enable;
+            if( /*val == 0 ||*/ (s->lcr & 0x3f) != (val & 0x3f)) {
             s->lcr = val;
             serial_update_parameters(s);
+            }
+            else
+                s->lcr = val;
             break_enable = (val >> 6) & 1;
             if (break_enable != s->last_break_enable) {
                 s->last_break_enable = break_enable;
+//              printf("serial: %s BREAK\n", break_enable == 0 ? "clear" : "set");
                 qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, 
                                &break_enable);
             }
         }
         break;
     case 4:
+       {   // MCR: only the lower 2 bits (DTR and RTS) are supported
+           int iBit = val & 3;
+           if((s->mcr & 3) != iBit)
+                qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_MSTAT, &iBit);
+        }
         s->mcr = val & 0x1f;
+//        printf("serial: modem control %x  DTR=%s  DSR=%s\n", val, val & 1 ? "on" : \
"off", val & 2 ? "on" : "off");  break;
     case 5:
         break;
@@ -256,6 +276,15 @@
             ret |= (s->mcr & 0x02) << 3;
             ret |= (s->mcr & 0x01) << 5;
         } else {
+            // TODO: should generate interrupts
+            int iBit;
+            qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_GET_MSTAT, &iBit);
+            if(iBit != -1) {
+                iBit &= 0xf0;
+                ret = (((s->msr ^ iBit)) >> 4) & 0xf;
+                // printf("TIO status  bits %x   old %x  delta %x\n", iBit, s->msr, \
ret); +                s->msr = iBit | ret;
+            }
             ret = s->msr;
         }
         break;
@@ -272,6 +301,7 @@
 static int serial_can_receive(SerialState *s)
 {
     return !(s->lsr & UART_LSR_DR);
+//    return (s->lsr & UART_LSR_DR) ? 0 : 16;
 }
 
 static void serial_receive_byte(SerialState *s, int ch)
@@ -311,7 +341,7 @@
 {
     SerialState *s = opaque;
 
-    qemu_put_8s(f,&s->divider);
+    qemu_put_be16(f,s->divider);
     qemu_put_8s(f,&s->rbr);
     qemu_put_8s(f,&s->ier);
     qemu_put_8s(f,&s->iir);
@@ -325,11 +355,15 @@
 static int serial_load(QEMUFile *f, void *opaque, int version_id)
 {
     SerialState *s = opaque;
+    int iBit;
 
-    if(version_id != 1)
+    if(version_id == 1)
+        s->divider = qemu_get_byte(f);
+    else if(version_id == 2)
+        s->divider = qemu_get_be16(f);
+    else
         return -EINVAL;
 
-    qemu_get_8s(f,&s->divider);
     qemu_get_8s(f,&s->rbr);
     qemu_get_8s(f,&s->ier);
     qemu_get_8s(f,&s->iir);
@@ -339,6 +373,10 @@
     qemu_get_8s(f,&s->msr);
     qemu_get_8s(f,&s->scr);
 
+    // restore last serial line state ...
+    serial_update_parameters(s);
+    iBit = s->mcr & 3;
+    qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_MSTAT, &iBit);
     return 0;
 }
 
@@ -357,7 +395,7 @@
     s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
     s->iir = UART_IIR_NO_INT;
 
-    register_savevm("serial", base, 1, serial_save, serial_load, s);
+    register_savevm("serial", base, 2, serial_save, serial_load, s);
 
     register_ioport_write(base, 8, 1, serial_ioport_write, s);
     register_ioport_read(base, 8, 1, serial_ioport_read, s);



_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic