server: Add support for pending write flag to the serial device. Take 2.

Dmitry Timoshkov dmitry at baikal.ru
Sun Oct 13 23:53:54 CDT 2013


This patch makes WaitCommEvent(EV_TXEMPTY) work when a serial device
reports that the out queue is empty even after a successful write()
which is the case for real COM ports and as well as for serial-USB
adapters.

Take 2 removes special handling of FlushFileBuffers for a serial sevice,
the tests show that it really should not reset the pending write state.

I'd like to see some feedback for this patch.
---
 dlls/kernel32/tests/comm.c |  5 -----
 dlls/ntdll/file.c          | 18 ++++++++++++++++++
 dlls/ntdll/serial.c        | 21 +++++++++++++--------
 server/protocol.def        |  3 +++
 server/serial.c            | 11 +++++++++++
 5 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/dlls/kernel32/tests/comm.c b/dlls/kernel32/tests/comm.c
index c444e90..18be0bd 100644
--- a/dlls/kernel32/tests/comm.c
+++ b/dlls/kernel32/tests/comm.c
@@ -857,7 +857,6 @@ todo_wine
     res = WaitCommEvent(hcom, &evtmask, &ovl_wait);
     ok(!res && GetLastError() == ERROR_IO_PENDING, "WaitCommEvent error %d\n", GetLastError());
     res = WaitForSingleObject(ovl_wait.hEvent, TIMEOUT);
-todo_wine
     ok(res == WAIT_OBJECT_0, "WaitCommEvent failed with a timeout\n");
     if (res == WAIT_OBJECT_0)
     {
@@ -879,15 +878,12 @@ todo_wine
         res = FALSE;
     }
     after = GetTickCount();
-todo_wine
     ok(res, "WaitCommEvent error %d\n", GetLastError());
-todo_wine
     ok(evtmask & EV_TXEMPTY, "WaitCommEvent: expected EV_TXEMPTY, got %#x\n", evtmask);
     CloseHandle(ovl_wait.hEvent);
 
     timediff = after - before;
     trace("WaitCommEvent for EV_TXEMPTY took %d ms (timeout %d)\n", timediff, TIMEOUT);
-todo_wine
     ok(timediff < 900, "WaitCommEvent used %d ms for waiting\n", timediff);
 
     res = WaitForSingleObject(ovl_write.hEvent, 0);
@@ -949,7 +945,6 @@ todo_wine
 
         res = WaitForSingleObject(ovl_wait.hEvent, TIMEOUT);
         if (i == 0)
-todo_wine
             ok(res == WAIT_OBJECT_0, "WaitCommEvent failed with a timeout\n");
         else
             ok(res == WAIT_TIMEOUT, "WaitCommEvent should fail with a timeout\n");
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index e747e8f..9dbc8fe 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -920,6 +920,20 @@ static NTSTATUS FILE_AsyncWriteService(void *user, IO_STATUS_BLOCK *iosb, NTSTAT
     return status;
 }
 
+static NTSTATUS set_pending_write( HANDLE device )
+{
+    NTSTATUS status;
+
+    SERVER_START_REQ( set_serial_info )
+    {
+        req->handle = wine_server_obj_handle( device );
+        req->flags  = SERIALINFO_PENDING_WRITE;
+        status = wine_server_call( req );
+    }
+    SERVER_END_REQ;
+    return status;
+}
+
 /******************************************************************************
  *  NtWriteFile					[NTDLL.@]
  *  ZwWriteFile					[NTDLL.@]
@@ -1146,6 +1160,10 @@ done:
 
 err:
     if (needs_close) close( unix_handle );
+
+    if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING))
+        set_pending_write( hFile );
+
     if (status == STATUS_SUCCESS)
     {
         io_status->u.Status = status;
diff --git a/dlls/ntdll/serial.c b/dlls/ntdll/serial.c
index 6738009..04aacf0 100644
--- a/dlls/ntdll/serial.c
+++ b/dlls/ntdll/serial.c
@@ -367,6 +367,7 @@ static NTSTATUS get_timeouts(HANDLE handle, SERIAL_TIMEOUTS* st)
     SERVER_START_REQ( get_serial_info )
     {
         req->handle = wine_server_obj_handle( handle );
+        req->flags = 0;
         if (!(status = wine_server_call( req )))
         {
             st->ReadIntervalTimeout         = reply->readinterval;
@@ -380,17 +381,19 @@ static NTSTATUS get_timeouts(HANDLE handle, SERIAL_TIMEOUTS* st)
     return status;
 }
 
-static NTSTATUS get_wait_mask(HANDLE hDevice, DWORD *mask, DWORD *cookie)
+static NTSTATUS get_wait_mask(HANDLE hDevice, DWORD *mask, DWORD *cookie, DWORD *pending_write)
 {
     NTSTATUS    status;
 
     SERVER_START_REQ( get_serial_info )
     {
         req->handle = wine_server_obj_handle( hDevice );
+        req->flags = pending_write ? SERIALINFO_PENDING_WRITE : 0;
         if (!(status = wine_server_call( req )))
         {
             *mask = reply->eventmask;
             if (cookie) *cookie = reply->cookie;
+            if (pending_write) *pending_write = reply->pending_write;
         }
     }
     SERVER_END_REQ;
@@ -797,6 +800,7 @@ typedef struct async_commio
     DWORD               evtmask;
     DWORD               cookie;
     DWORD               mstat;
+    DWORD               pending_write;
     serial_irq_info     irq_info;
 } async_commio;
 
@@ -855,7 +859,7 @@ static NTSTATUS get_irq_info(int fd, serial_irq_info *irq_info)
 static DWORD check_events(int fd, DWORD mask,
                           const serial_irq_info *new,
                           const serial_irq_info *old,
-                          DWORD new_mstat, DWORD old_mstat)
+                          DWORD new_mstat, DWORD old_mstat, DWORD pending_write)
 {
     DWORD ret = 0, queue;
 
@@ -887,7 +891,7 @@ static DWORD check_events(int fd, DWORD mask,
     }
     if (mask & EV_TXEMPTY)
     {
-        if (!old->temt && new->temt)
+        if ((!old->temt || pending_write) && new->temt)
             ret |= EV_TXEMPTY;
     }
     return ret & mask;
@@ -934,9 +938,9 @@ static DWORD CALLBACK wait_for_event(LPVOID arg)
             }
             *commio->events = check_events(fd, commio->evtmask,
                                            &new_irq_info, &commio->irq_info,
-                                           new_mstat, commio->mstat);
+                                           new_mstat, commio->mstat, commio->pending_write);
             if (*commio->events) break;
-            get_wait_mask(commio->hDevice, &dummy, &cookie);
+            get_wait_mask(commio->hDevice, &dummy, &cookie, (commio->evtmask & EV_TXEMPTY) ? &commio->pending_write : NULL);
             if (commio->cookie != cookie)
             {
                 *commio->events = 0;
@@ -975,7 +979,8 @@ static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, PIO_STATUS_BLOCK
     commio->events  = events;
     commio->iosb    = piosb;
     commio->hEvent  = hEvent;
-    get_wait_mask(commio->hDevice, &commio->evtmask, &commio->cookie);
+    commio->pending_write = 0;
+    get_wait_mask(commio->hDevice, &commio->evtmask, &commio->cookie, (commio->evtmask & EV_TXEMPTY) ? &commio->pending_write : NULL);
 
 /* We may never return, if some capabilities miss
  * Return error in that case
@@ -1022,7 +1027,7 @@ static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, PIO_STATUS_BLOCK
     /* We might have received something or the TX buffer is delivered */
     *events = check_events(fd, commio->evtmask,
                                &commio->irq_info, &commio->irq_info,
-                               commio->mstat, commio->mstat);
+                               commio->mstat, commio->mstat, commio->pending_write);
     if (*events)
     {
         status = STATUS_SUCCESS;
@@ -1170,7 +1175,7 @@ static inline NTSTATUS io_control(HANDLE hDevice,
     case IOCTL_SERIAL_GET_WAIT_MASK:
         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
         {
-            if (!(status = get_wait_mask(hDevice, lpOutBuffer, NULL)))
+            if (!(status = get_wait_mask(hDevice, lpOutBuffer, NULL, NULL)))
                 sz = sizeof(DWORD);
         }
         else
diff --git a/server/protocol.def b/server/protocol.def
index a7c5478..95d7994 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2212,6 +2212,7 @@ enum message_type
 /* Retrieve info about a serial port */
 @REQ(get_serial_info)
     obj_handle_t handle;       /* handle to comm port */
+    int          flags;
 @REPLY
     unsigned int readinterval;
     unsigned int readconst;
@@ -2220,6 +2221,7 @@ enum message_type
     unsigned int writemult;
     unsigned int eventmask;
     unsigned int cookie;
+    unsigned int pending_write;
 @END
 
 
@@ -2236,6 +2238,7 @@ enum message_type
 @END
 #define SERIALINFO_SET_TIMEOUTS  0x01
 #define SERIALINFO_SET_MASK      0x02
+#define SERIALINFO_PENDING_WRITE 0x04
 
 
 /* Create an async I/O */
diff --git a/server/serial.c b/server/serial.c
index 28f17f4..25587a7 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -77,6 +77,7 @@ struct serial
 
     unsigned int        eventmask;
     unsigned int        generation; /* event mask change counter */
+    unsigned int        pending_write;
 
     struct termios      original;
 
@@ -137,6 +138,7 @@ struct object *create_serial( struct fd *fd )
     serial->writeconst   = 0;
     serial->eventmask    = 0;
     serial->generation   = 0;
+    serial->pending_write = 0;
     serial->fd = (struct fd *)grab_object( fd );
     set_fd_user( fd, &serial_fd_ops, &serial->obj );
     return &serial->obj;
@@ -214,6 +216,11 @@ DECL_HANDLER(get_serial_info)
         reply->eventmask    = serial->eventmask;
         reply->cookie       = serial->generation;
 
+        /* pending write */
+        reply->pending_write = serial->pending_write;
+        if (req->flags & SERIALINFO_PENDING_WRITE)
+            serial->pending_write = 0;
+
         release_object( serial );
     }
 }
@@ -234,6 +241,10 @@ DECL_HANDLER(set_serial_info)
             serial->writemult    = req->writemult;
         }
 
+        /* pending write */
+        if (req->flags & SERIALINFO_PENDING_WRITE)
+            serial->pending_write = 1;
+
         /* event mask */
         if (req->flags & SERIALINFO_SET_MASK)
         {
-- 
1.8.3.4




More information about the wine-patches mailing list