Implement WaitCommEvent by polling the appropraite data sources

Uwe Bonnes bon at elektron.ikp.physik.tu-darmstadt.de
Sun Jul 31 09:20:16 CDT 2005


Changelog:
	dlls/kernel/comm.c:
	Implement WaitCommEvent by polling the appropraite data sources

WaitCommEvent may wait asynchronous on a combination of a pletora of
events. No single Linux Kernel Call (select() on the file selector,
TIOCMIWAIT etc)  provides the same functionality, however the
information is available from different sources, but not as an event, so it
must be polled. 

-- 
Uwe Bonnes                bon at elektron.ikp.physik.tu-darmstadt.de

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
Index: wine/dlls/kernel/comm.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/comm.c,v
retrieving revision 1.94
diff -u -r1.94 comm.c
--- wine/dlls/kernel/comm.c	16 May 2005 17:52:11 -0000	1.94
+++ wine/dlls/kernel/comm.c	31 Jul 2005 13:42:07 -0000
@@ -19,6 +19,9 @@
  *
  * History:
  *
+ * Jul 31, 2005. Uwe Bonnes <bon at elektron.ikp.physik.tu-darmstadt.de>
+ * - Implement WaitCommEvent() by polling the appropriate sources
+ *
  * Apr 3, 1999.  Lawson Whitney <lawson_whitney at juno.com>
  * - Fixed the modem control part of EscapeCommFunction16.
  *
@@ -121,19 +124,31 @@
 {
     wine_server_release_fd( handle, fd );
 }
-
-
+/*             serial_irq_info
+ * local structure holding the irq values we need for WaitCommEvent()
+ *
+ * Stripped down from struct serial_icounter_struct, which may not be available on some systems
+ * As the modem line interrupts (cts, dsr, rng, dcd) only get updated with TIOCMIWAIT active, 
+ * no need to carry them in the internal structure
+ *
+ */
+typedef struct serial_irq_info
+{
+    int rx , tx, frame, overrun, parity, brk, buf_overrun;
+    
+}serial_irq_info;
 /***********************************************************************
- * Asynchronous I/O for asynchronous wait requests                     *
+ * Data needed by the Thread polling for the changing CommEvent        *
  */
 
 typedef struct async_commio
 {
     HANDLE              handle;
-    PIO_APC_ROUTINE     apc_internal;
-    int                 type;
     char*               buffer;
-    int                 fd;
+    HANDLE              hEvent;
+    DWORD               evtmask;
+    DWORD               mstat;
+    serial_irq_info     irq_info;
 } async_commio;
 
 /***********************************************************************/
@@ -142,6 +157,36 @@
 #define	TIOCINQ FIONREAD
 #endif
 
+/***********************************************************************
+ *   Get externded interrupt count info, Needed for WaitCommEvent
+ */
+static int COMM_GetEInfo(int fd, serial_irq_info *irq_info)
+{
+    memset(irq_info,0, sizeof(serial_irq_info));
+#ifdef TIOCGICOUNT
+    struct serial_icounter_struct einfo;
+    if( ioctl(fd,TIOCGICOUNT, &einfo))
+    {
+	TRACE("TIOCGICOUNT failed\n");
+	return -1;
+    }
+    else
+    {
+	irq_info->rx          = einfo.rx;
+	irq_info->tx          = einfo.tx;
+	irq_info->frame       = einfo.frame;
+	irq_info->overrun     = einfo.overrun;
+	irq_info->parity      = einfo.parity;
+	irq_info->brk         = einfo.brk;
+	irq_info->buf_overrun = einfo.buf_overrun;
+	return 0;
+    }
+#else
+    TRACE("not available\n");
+    return -1;
+#endif
+}
+
 static int COMM_WhackModem(int fd, unsigned int andy, unsigned int orrie)
 {
 #ifdef TIOCMGET
@@ -1907,33 +1952,102 @@
 #endif
 }
 
-/***********************************************************************
- *             COMM_WaitCommEventService      (INTERNAL)
- *
- *  This function is called while the client is waiting on the
- *  server, so we can't make any server calls here.
- */
-static void WINAPI COMM_WaitCommEventService(void* ovp, IO_STATUS_BLOCK* iosb, ULONG status)
+static DWORD WINAPI Comm_CheckEvents(int fd, DWORD mask, serial_irq_info *new, serial_irq_info *old, DWORD new_mstat, DWORD old_mstat)
 {
-    async_commio *commio = (async_commio*) ovp;
-
-    TRACE("iosb %p\n", iosb);
+    DWORD ret = 0, queue;
 
-    switch (status)
+    TRACE("mask 0x%08lx\n", mask);
+    TRACE("old->rx          0x%08x vs. new->rx          0x%08x \n", old->rx, new->rx);
+    TRACE("old->tx          0x%08x vs. new->tx          0x%08x \n", old->tx, new->tx);
+    TRACE("old->frame       0x%08x vs. new->frame       0x%08x \n", old->frame, new->frame);
+    TRACE("old->overrun     0x%08x vs. new->overrun     0x%08x \n", old->overrun, new->overrun);
+    TRACE("old->parity      0x%08x vs. new->parity      0x%08x \n", old->parity, new->parity);
+    TRACE("old->brk         0x%08x vs. new->brk         0x%08x \n", old->brk, new->brk);
+    TRACE("old->buf_overrun 0x%08x vs. new->buf_overrun 0x%08x \n", old->buf_overrun, new->buf_overrun);
+
+    ret |= ((mask & EV_BREAK) && ( old->brk != new->brk))?EV_BREAK:0;
+    ret |= ((mask & EV_CTS  ) && ((old_mstat&MS_CTS_ON )!=(new_mstat&MS_CTS_ON )))?EV_CTS  :0;
+    ret |= ((mask & EV_DSR  ) && ((old_mstat&MS_DSR_ON )!=(new_mstat&MS_DSR_ON )))?EV_DSR  :0;
+    ret |= ((mask & EV_RING ) && ((old_mstat&MS_RING_ON)!=(new_mstat&MS_RING_ON)))?EV_RING :0;
+    ret |= ((mask & EV_RLSD ) && ((old_mstat&MS_RLSD_ON)!=(new_mstat&MS_RLSD_ON)))?EV_RLSD :0;
+    ret |= ((mask & EV_ERR  ) && (( old->frame != new->frame) ||(old->overrun != new->overrun)
+		|| (old->parity != new->parity)) )?EV_ERR  :0;
+    if (mask & EV_RXCHAR)
+    {
+	queue = 0;
+#ifdef TIOCINQ
+	if(ioctl(fd, TIOCINQ, &queue))
+	    WARN("TIOCINQ returned error\n");
+#endif
+	if (queue)
+	    ret |= EV_RXCHAR;
+    }
+    if (mask & EV_TXEMPTY)
     {
-    case STATUS_ALERTED: /* got some new stuff */
-        /* FIXME: detect other events */
-        *commio->buffer = EV_RXCHAR;
-        iosb->u.Status = STATUS_SUCCESS;
-        break;
-    default:
-        iosb->u.Status = status;
-        break;
+	queue = 0;
+/* We really want to know when all characters have gone out of the transmitter */
+#if defined(TIOCSERGETLSR) 
+	if(ioctl(fd, TIOCSERGETLSR, &queue))
+	    WARN("TIOCSERGETLSR returned error\n");
+	if (queue)
+/* TIOCINQ only checks for an empty buffer */
+#elif def(TIOCINQ)
+	if(ioctl(fd, TIOCOUTQ, &queue))
+	    WARN("TIOCOUTQ returned error\n");
+	if (!queue)
+#endif
+           ret |= EV_TXEMPTY;
     }
-    wine_server_release_fd( commio->handle, commio->fd );
-    if ( ((LPOVERLAPPED)iosb)->hEvent != INVALID_HANDLE_VALUE )
-        NtSetEvent( ((LPOVERLAPPED)iosb)->hEvent, NULL );
+    TRACE("OUTQUEUE %ld, Transmitter %sempty\n", queue, (ret & EV_TXEMPTY)?"":"not ");
+    return ret;
+    
+}
+/***********************************************************************
+ *             COMM_WaitCommEventService      (INTERNAL)
+ *
+ *  We need to poll for what is interesting
+ *  TIOCMIWAIT only checks modem status line and may not be aborted by a changing mask 
+ *  
+ */
+static DWORD WINAPI COMM_WaitCommEventService(LPVOID arg)
+{
+    async_commio *commio = (async_commio*) arg;
+    int waitmask = 0;
+    int rc, fd, abort;
+    serial_irq_info new_irq_info;
+    DWORD new_mstat, new_evtmask;
+
+    fd=get_comm_fd( commio->handle, GENERIC_READ );
+
+    TRACE("handle %p fd 0x%08x, mask 0x%08lx buffer %p event %p irq_info %p waitmask 0x%08x\n", 
+	  commio->handle, fd, commio->evtmask, commio->buffer, commio->hEvent, &commio->irq_info, waitmask);
+    do
+    {
+	/*
+	 * TIOCMIWAIT is not adequate
+	 *
+	 * FIXME:
+	 * We don't handle the EV_RXFLAG (the eventchar)
+	 */
+	Sleep(1);
+	rc= COMM_GetEInfo(fd,&new_irq_info);
+	if (rc)
+	    TRACE("TIOCGICOUNT err %s\n", strerror(errno));
+	rc = GetCommModemStatus(commio->handle, &new_mstat);
+	if (!rc)
+	    TRACE("GetCommModemStatus failed\n");
+	rc = Comm_CheckEvents(fd, commio->evtmask,&new_irq_info,&commio->irq_info, new_mstat, commio->mstat);
+	GetCommMask(commio->handle, &new_evtmask);
+	abort = (commio->evtmask != new_evtmask);
+	TRACE("resulting Eventmask 0x%08x\n", rc);
+    } while (!rc && ! abort);
+    if (abort) rc = 0;
+    release_comm_fd( commio->handle, fd );
+    *commio->buffer = rc;
+    if (commio->hEvent != INVALID_HANDLE_VALUE )
+        NtSetEvent( commio->hEvent, NULL );
     HeapFree(GetProcessHeap(), 0, commio );
+    return 0;
 }
 
 
@@ -1949,7 +2063,8 @@
 {
     int                 fd;
     async_commio*       commio;
-    NTSTATUS            status;
+    DWORD               result_mask;
+    BOOL res;
 
     if (!lpOverlapped)
     {
@@ -1971,32 +2086,70 @@
     }
 
     commio->handle = hFile;
-    commio->type = ASYNC_TYPE_WAIT;
-    commio->apc_internal = COMM_WaitCommEventService;
     commio->buffer = (char *)lpdwEvents;
-    commio->fd = fd;  /* FIXME */
+    commio->hEvent = lpOverlapped->hEvent;
+    GetCommMask(hFile, &commio->evtmask);
 
-    lpOverlapped->InternalHigh = 0;
-    lpOverlapped->u.s.Offset = 0;
-    lpOverlapped->u.s.OffsetHigh = 0;
-
-    SERVER_START_REQ( register_async )
+/* We may never return, if some capabilities miss
+ * Return error in that case
+ */
+#if !defined(TIOCINQ)
+    if(commio->evtmask & EV_RXCHAR)
+	goto error;
+#endif
+#if !(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)
+    if(commio->evtmask & EV_TXEMPTY)
+	goto error;
+#endif
+#if !defined(TIOCMGET)
+    if(commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD))
+	goto error;
+#endif
+#if !defined(TIOCM_CTS)
+    if(commio->evtmask & EV_CTS)
+	goto error;
+#endif
+#if !defined(TIOCM_DSR)
+    if(commio->evtmask & EV_DSR)
+	goto error;
+#endif
+#if !defined(TIOCM_RNG)
+    if(commio->evtmask & EV_RING)
+	goto error;
+#endif
+#if !defined(TIOCM_CAR)
+    if(commio->evtmask & EV_RLSD)
+	goto error;
+#endif
+    if(commio->evtmask & EV_RXFLAG)
+	FIXME("EV_RXFLAG not handled\n");
+    COMM_GetEInfo(fd,&commio->irq_info);
+    GetCommModemStatus(hFile, &commio->mstat);
+    /* We might have received something or the TX bufffer is delivered*/
+    result_mask = Comm_CheckEvents( fd, commio->evtmask, &commio->irq_info, &commio->irq_info,commio->mstat,commio->mstat);
+    if (result_mask) 
     {
-        req->handle = hFile;
-        req->io_apc = COMM_WaitCommEventService;
-        req->io_user = commio;
-        req->io_sb = (IO_STATUS_BLOCK*)lpOverlapped;
-        req->count = 0;
-        status = wine_server_call( req );
+	TRACE("Event already meat\n");
+	*lpdwEvents = result_mask;
+	release_comm_fd( commio->handle, fd );
+	HeapFree(GetProcessHeap(), 0, commio );
+	res = TRUE;
     }
-    SERVER_END_REQ;
-
-    if ( status ) SetLastError( RtlNtStatusToDosError(status) );
-    else NtCurrentTeb()->num_async_io++;
-
+    else
+    {
+	CreateThread(NULL, 0, COMM_WaitCommEventService, (LPVOID)commio, 0, NULL);
+	SetLastError(ERROR_IO_PENDING);
+	res = FALSE;
+    }
+    return res;
+#if !defined(TIOCINQ) || (!(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) && !defined(TIOCINQ)) || !defined(TIOCMGET) || !defined(TIOCM_CTS) ||!defined(TIOCM_DSR) || !defined(TIOCM_RNG) || !defined(TIOCM_CAR)
+ error:
+    FIXME("Returning error because of missing capabitilies of the System\n");
+    HeapFree(GetProcessHeap(), 0, commio );
+    SetLastError(ERROR_INVALID_PARAMETER);
     return FALSE;
+#endif
 }
-
 /***********************************************************************
  *           WaitCommEvent   (KERNEL32.@)
  *
@@ -2020,7 +2173,8 @@
     LPOVERLAPPED lpOverlapped) /* [in/out] for Asynchronous waiting */
 {
     OVERLAPPED ov;
-    int ret;
+    int ret = 0;
+    DWORD res, err;
 
     TRACE("(%p %p %p )\n",hFile, lpdwEvents,lpOverlapped);
 
@@ -2030,10 +2184,27 @@
     /* if there is no overlapped structure, create our own */
     ov.hEvent = CreateEventW(NULL,FALSE,FALSE,NULL);
 
-    COMM_WaitCommEvent(hFile, lpdwEvents, &ov);
-
-    /* wait for the overlapped to complete */
-    ret = GetOverlappedResult(hFile, &ov, NULL, TRUE);
+    res = COMM_WaitCommEvent(hFile, lpdwEvents, &ov);
+    err = GetLastError();
+    if (!res)
+    {
+	if (err == ERROR_IO_PENDING)
+	{
+	    do 
+	    {
+		res = WaitForSingleObjectEx(ov.hEvent, INFINITE, FALSE);
+	    } while (res != WAIT_OBJECT_0);
+	    TRACE("Event met\n:");
+	    ret = TRUE;
+	}
+	else
+	{
+	    FIXME("Unknown error 0x%08lx\n", err);
+	    ret = FALSE;
+	}
+    }
+    else
+	ret = TRUE;
     CloseHandle(ov.hEvent);
 
     return ret;



More information about the wine-patches mailing list