- created server object for handling async i/o
- implemented WaitCommEvent with the EV_RXCHAR flag
- implemented GetOverlappedResult

diff --git a/dlls/kernel/comm.c b/dlls/kernel/comm.c
index c6da61c..b100e1d 100644
--- a/dlls/kernel/comm.c
+++ b/dlls/kernel/comm.c
@@ -2580,13 +2580,111 @@
 	return FALSE;
 #endif
 }
+
+VOID COMM_WaitCommEventService(void **args)
+{
+    LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)args[0];
+    LPDWORD buffer = (LPDWORD)args[1];
+    DWORD events = (DWORD)args[2];
+
+    TRACE("overlapped %p wait complete %p <- %lx\n",lpOverlapped,buffer,events);
+    if(buffer)
+        *buffer = events;
+ 
+    SetEvent( lpOverlapped->hEvent);
+}
+
 /***********************************************************************
  *           WaitCommEvent   (KERNEL32.719)
+ *
+ * Wait until something interesting happens on a COMM port.
+ * Interesting things (events) are set by calling SetCommMask before
+ * this function is called.
+ *
+ * RETURNS:
+ *   TRUE if successful
+ *   FALSE if failure
+ *
+ *   The set of detected events will be written to *lpdwEventMask
+ *   ERROR_IO_PENDING will be returned the overlapped structure was passed
+ *
+ * BUGS:
+ *  Only supports EV_RXCHAR and EV_TXEMPTY
  */
-BOOL WINAPI WaitCommEvent(HANDLE hFile,LPDWORD eventmask ,LPOVERLAPPED overlapped)
+BOOL WINAPI WaitCommEvent(
+    HANDLE hFile,             /* [I] handle of comm port to wait for */
+    LPDWORD lpdwEvents,       /* [O] event(s) that were detected */
+    LPOVERLAPPED lpOverlapped /* [I/O] for Asynchronous waiting */
+) {
+    OVERLAPPED ov;
+    LPOVERLAPPED lpov;
+    int ret;
+
+    TRACE("(%x %p %p )\n",hFile, lpdwEvents,lpOverlapped);
+
+    /* if there is no overlapped structure, create our own */
+    if(!lpOverlapped)
+    {
+        ov.hEvent = CreateEventA(NULL,FALSE,FALSE,NULL);
+        lpov = &ov;
+    }
+    else
+        lpov = lpOverlapped;
+
+    /* check that the overlapped structure has a valid event flag */
+    if ( (lpov->hEvent==0) || (lpov->hEvent == INVALID_HANDLE_VALUE) )
 {
-	FIXME("(%d %p %p )\n",hFile, eventmask,overlapped);
-	return TRUE;
+        ERR("Couldn't create Event flag for Overlapped structure\n");
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    lpov->Internal = 0;
+    lpov->InternalHigh = hFile;
+    lpov->Offset = 0;
+    lpov->OffsetHigh = 0;
+
+    /* start an ASYNCHRONOUS WaitCommEvent */
+    SERVER_START_REQ
+    {
+        struct create_async_request *req = server_alloc_req( sizeof(*req), 0 );
+
+        req->file_handle = hFile;
+        req->overlapped  = lpov;
+        req->buffer = lpdwEvents;
+        req->count = 0;
+        req->func = COMM_WaitCommEventService;
+        req->type = ASYNC_TYPE_WAIT;
+
+        ret=server_call( REQ_CREATE_ASYNC );
+
+        lpov->Internal = req->ov_handle;
+    }
+    SERVER_END_REQ;
+
+    if(ret)
+    {
+        if(!lpOverlapped)
+            CloseHandle(lpov->hEvent);
+        TRACE("server call failed.\n");
+        return FALSE;
+    }
+
+    /* wait ourselves if the caller didn't give us an overlapped struct */
+    if(!lpOverlapped)
+    {
+        GetOverlappedResult(hFile, lpov, NULL, TRUE);
+        CloseHandle(lpov->hEvent);
+        lpov->hEvent=0;
+    }
+    else
+    {
+        /* caller wants overlapped I/O using GetOverlapped result */
+        SetLastError(ERROR_IO_PENDING);
+        return FALSE;
+    }
+
+    return TRUE;
 }
 
 /***********************************************************************
diff --git a/include/server.h b/include/server.h
index c6c7d57..c069f43 100644
--- a/include/server.h
+++ b/include/server.h
@@ -356,7 +356,7 @@
     OUT int          type;         /* function type */
     OUT VARARG(args,ptrs);         /* function arguments */
 };
-enum apc_type { APC_NONE, APC_USER, APC_TIMER };
+enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC };
 
 
 /* Close a handle for the current process */
@@ -1332,6 +1332,32 @@
 #define SERIALINFO_SET_MASK      0x02
 #define SERIALINFO_SET_ERROR     0x04
 
+struct create_async_request
+{
+    REQUEST_HEADER;                /* request header */
+    IN  int          file_handle;  /* handle to comm port */
+    IN  void*        overlapped;
+    IN  void*        buffer;
+    IN  int          count;
+    IN  void*        func;
+    IN  int          type;
+    OUT int          ov_handle;
+};
+#define ASYNC_TYPE_READ  0x01
+#define ASYNC_TYPE_WRITE 0x02
+#define ASYNC_TYPE_WAIT  0x03
+
+/*
+ * Used by service thread to tell the server that the current
+ * operation has completed
+ */
+struct async_result_request
+{
+    REQUEST_HEADER;                /* request header */
+    IN  int          ov_handle;
+    IN  int          result;       /* NT status code */
+};
+
 /* Everything below this line is generated automatically by tools/make_requests */
 /* ### make_requests begin ### */
 
@@ -1445,6 +1471,8 @@
     REQ_CREATE_SERIAL,
     REQ_GET_SERIAL_INFO,
     REQ_SET_SERIAL_INFO,
+    REQ_CREATE_ASYNC,
+    REQ_ASYNC_RESULT,
     REQ_NB_REQUESTS
 };
 
@@ -1560,9 +1588,11 @@
     struct create_serial_request create_serial;
     struct get_serial_info_request get_serial_info;
     struct set_serial_info_request set_serial_info;
+    struct create_async_request create_async;
+    struct async_result_request async_result;
 };
 
-#define SERVER_PROTOCOL_VERSION 26
+#define SERVER_PROTOCOL_VERSION 27
 
 /* ### make_requests end ### */
 /* Everything above this line is generated automatically by tools/make_requests */
diff --git a/scheduler/synchro.c b/scheduler/synchro.c
index 52f112b..f5d4470 100644
--- a/scheduler/synchro.c
+++ b/scheduler/synchro.c
@@ -46,6 +46,9 @@
         {
         case APC_NONE:
             return;  /* no more APCs */
+        case APC_ASYNC:
+            proc( &args[0] );
+            break;
         case APC_USER:
             proc( args[0] );
             break;
diff --git a/server/Makefile.in b/server/Makefile.in
index a7779f3..6f1c36a 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -6,6 +6,7 @@
 MODULE    = none
 
 C_SRCS = \
+	async.c \
 	atom.c \
 	change.c \
 	console.c \
diff --git a/server/async.c b/server/async.c
new file mode 100644
index 0000000..379d130
--- /dev/null
+++ b/server/async.c
@@ -0,0 +1,270 @@
+/*
+ * Server-side support for async i/o operations
+ *
+ * Copyright (C) 1998 Alexandre Julliard
+ * Copyright (C) 2000 Mike McCormack
+ *
+ * TODO:
+ *  Fix up WaitCommEvent operations. Currently only EV_RXCHAR is supported.
+ *    This may require modifications to the linux kernel to enable select
+ *    to wait on Modem Status Register deltas. (delta DCD, CTS, DSR or RING)
+ *
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "winerror.h"
+#include "winbase.h"
+
+#include "handle.h"
+#include "thread.h"
+#include "request.h"
+
+struct async 
+{
+    struct object           obj;
+    void                   *client_overlapped;
+    int                     type;
+    int                     result;
+    int                     count;
+    int                     eventmask;
+    struct async           *next;
+    struct timeout_user    *timeout;
+    struct wait_queue_entry wait;
+    void                   *buffer;
+    void                   *func;
+    struct thread          *thread;
+    struct object          *file;
+};
+
+static void async_dump( struct object *obj, int verbose );
+static void async_destroy( struct object *obj );
+static int async_get_poll_events( struct object *obj );
+static int async_get_read_fd( struct object *obj );
+static int async_get_write_fd( struct object *obj );
+static int async_get_info( struct object *obj, struct get_file_info_request *req );
+static void async_poll_event( struct object *obj, int event );
+
+static const struct object_ops async_ops =
+{
+    sizeof(struct async),         /* size */
+    async_dump,                   /* dump */
+    default_poll_add_queue,       /* add_queue */
+    default_poll_remove_queue,    /* remove_queue */
+    default_poll_signaled,        /* signaled */
+    no_satisfied,                 /* satisfied */
+    async_get_poll_events,        /* get_poll_events */
+    async_poll_event,             /* poll_event */
+    async_get_read_fd,            /* get_read_fd */
+    async_get_write_fd,           /* get_write_fd */
+    no_flush,                     /* flush */
+    async_get_info,               /* get_file_info */
+    async_destroy                 /* destroy */
+};
+
+static void async_dump( struct object *obj, int verbose )
+{
+    struct async *ov = (struct async *)obj;
+
+    assert( obj->ops == &async_ops );
+
+    fprintf( stderr, "async: overlapped %p %s\n", 
+                 ov->client_overlapped, ov->timeout?"with timeout":"");
+}
+
+/* same as file_destroy, but don't delete comm ports */
+static void async_destroy( struct object *obj )
+{
+    struct async *ov = (struct async *)obj;
+    assert( obj->ops == &async_ops );
+
+    if(ov->timeout)
+        remove_timeout_user(ov->timeout);
+    ov->timeout = NULL;
+}
+
+struct async *get_async_obj( struct process *process, int handle, unsigned int access )
+{
+    return (struct async *)get_handle_obj( process, handle, access, &async_ops );
+}
+
+static int async_get_poll_events( struct object *obj )
+{
+    struct async *ov = (struct async *)obj;
+    assert( obj->ops == &async_ops );
+
+    /* FIXME: this should be a function pointer */
+    return serial_async_get_poll_events(ov);
+}
+
+static int async_get_read_fd( struct object *obj )
+{
+    struct async *async = (struct async *)obj;
+    assert( obj->ops == &async_ops );
+    return dup( async->obj.fd );
+}
+
+static int async_get_write_fd( struct object *obj )
+{
+    struct async *async = (struct async *)obj;
+    assert( obj->ops == &async_ops );
+    return dup( async->obj.fd );
+}
+
+static int async_get_info( struct object *obj, struct get_file_info_request *req ) {
+    assert( obj->ops == &async_ops );
+    req->type        = FILE_TYPE_CHAR;
+    req->attr        = 0;
+    req->access_time = 0;
+    req->write_time  = 0;
+    req->size_high   = 0;
+    req->size_low    = 0;
+    req->links       = 0;
+    req->index_high  = 0;
+    req->index_low   = 0;
+    req->serial      = 0;
+    return 1;
+}
+
+/* data access functions */
+int async_type(struct async *ov)
+{
+    return ov->type;
+}
+
+int async_count(struct async *ov)
+{
+    return ov->count;
+}
+
+int async_get_eventmask(struct async *ov)
+{
+    return ov->eventmask;
+}
+
+int async_set_eventmask(struct async *ov, int eventmask)
+{
+    return ov->eventmask = eventmask;
+}
+
+DECL_HANDLER(create_async)
+{
+    struct object *obj;
+    struct async *ov = NULL;
+    int fd;
+
+    req->ov_handle = -1;
+    if (!(obj = get_handle_obj( current->process, req->file_handle, 0, NULL)) )
+        return;
+
+    fd = dup(obj->fd);
+    if(fd<0)
+    {
+        release_object(obj);
+        set_error(STATUS_UNSUCCESSFUL);
+        return;
+    }
+
+    if(0>fcntl(fd, F_SETFL, O_NONBLOCK))
+    {
+        release_object(obj);
+        set_error(STATUS_UNSUCCESSFUL);
+        return;
+    }
+
+    ov = alloc_object (&async_ops, fd);
+    if(!ov)
+    {
+        release_object(obj);
+        set_error(STATUS_UNSUCCESSFUL);
+        return;
+    }
+
+    ov->client_overlapped = req->overlapped;
+    ov->next    = NULL;
+    ov->timeout = NULL;
+    ov->type    = req->type;
+    ov->thread  = current;
+    ov->func    = req->func;
+    ov->file    = obj;
+    ov->buffer  = req->buffer;
+    ov->count   = req->count;
+
+    /* FIXME: this should be a function pointer */
+    serial_async_setup(obj,ov);
+
+    ov->obj.ops->add_queue(&ov->obj,&ov->wait);
+
+    req->ov_handle = alloc_handle( current->process, ov, GENERIC_READ|GENERIC_WRITE, 0 );
+
+    release_object(obj);
+}
+
+/* handler for async poll() events */
+static void async_poll_event( struct object *obj, int event )
+{
+    struct async *ov = (struct async *) obj;
+ 
+    /* queue an APC in the client thread to do our dirty work */
+    ov->obj.ops->remove_queue(&ov->obj,&ov->wait);
+
+    /* FIXME: this should be a function pointer */
+    event = serial_async_poll_event(obj,event);
+
+    thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 3,
+                     ov->client_overlapped, ov->buffer, event);
+} 
+
+/* handler for async i/o timeouts */
+static void overlapped_timeout (void *private)
+{
+    struct async *ov = (struct async *) private;
+ 
+    ov->obj.ops->remove_queue(&ov->obj,&ov->wait);
+ 
+    thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 3,
+                     ov->client_overlapped,ov->buffer, 0);
+}
+
+void async_add_timeout(struct async *ov, int timeout)
+{
+    struct timeval tv;
+
+    gettimeofday(&tv,0);
+    add_timeout(&tv,timeout);
+    ov->timeout = add_timeout_user(&tv, overlapped_timeout, ov);
+}
+
+DECL_HANDLER(async_result)
+{
+    struct async *ov;
+
+    if ((ov = get_async_obj( current->process, req->ov_handle, 0 )))
+    {
+        ov->result = req->result;
+        if(ov->result == STATUS_PENDING)
+        {
+            ov->obj.ops->add_queue(&ov->obj,&ov->wait);
+        }
+        release_object( ov );
+    }
+}
+
diff --git a/server/object.h b/server/object.h
index ab52ed6..746b7f6 100644
--- a/server/object.h
+++ b/server/object.h
@@ -25,6 +25,7 @@
 struct process;
 struct file;
 struct wait_queue_entry;
+struct async;
 
 /* operations valid on all objects */
 struct object_ops
@@ -156,6 +157,20 @@
 extern struct file *create_temp_file( int access );
 extern void file_set_error(void);
 
+/* async functions */
+
+void async_add_timeout(struct async *ov, int timeout);
+int async_count(struct async *ov);
+int async_type(struct async *ov);
+int async_get_eventmask(struct async *ov);
+int async_set_eventmask(struct async *ov, int eventmask);
+
+/* serial functions */
+
+int serial_async_setup(struct object *obj, struct async *ov);
+int serial_async_get_poll_events( struct async *ov );
+int serial_async_poll_event(struct object *obj, int event);
+
 /* console functions */
 
 extern int alloc_console( struct process *process );
diff --git a/server/request.h b/server/request.h
index ac80b7b..14dc2e3 100644
--- a/server/request.h
+++ b/server/request.h
@@ -202,6 +202,8 @@
 DECL_HANDLER(create_serial);
 DECL_HANDLER(get_serial_info);
 DECL_HANDLER(set_serial_info);
+DECL_HANDLER(create_async);
+DECL_HANDLER(async_result);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -316,6 +318,8 @@
     (req_handler)req_create_serial,
     (req_handler)req_get_serial_info,
     (req_handler)req_set_serial_info,
+    (req_handler)req_create_async,
+    (req_handler)req_async_result,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/serial.c b/server/serial.c
index d102233..c549bec 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -182,6 +182,77 @@
     return 1;
 }
 
+/* these functions are for interaction with asynchronous i/o objects */
+int serial_async_setup(struct object *obj, struct async *ov)
+{
+    struct serial *serial = (struct serial *)obj;
+    int timeout;
+
+    if(obj->ops != &serial_ops)
+        return 0;
+
+    switch(async_type(ov))
+    {
+    case ASYNC_TYPE_READ:
+        timeout = serial->readconst + serial->readmult*async_count(ov);
+        async_add_timeout(ov, timeout);
+        async_set_eventmask(ov,EV_RXCHAR);
+        break;
+    case ASYNC_TYPE_WRITE:
+        timeout = serial->writeconst + serial->writemult*async_count(ov);
+        async_add_timeout(ov, timeout);
+        async_set_eventmask(ov,EV_TXEMPTY);
+        break;
+    case ASYNC_TYPE_WAIT:
+        async_set_eventmask(ov,serial->eventmask);
+        break;
+    }
+
+    return 1;
+}
+
+int serial_async_get_poll_events( struct async *ov )
+{
+    int events=0,mask;
+
+    switch(async_type(ov))
+    {
+    case ASYNC_TYPE_READ:
+        events |= POLLIN;
+        break;
+    case ASYNC_TYPE_WRITE:
+        events |= POLLOUT;
+        break;
+    case ASYNC_TYPE_WAIT:
+    /* 
+     * FIXME: here is the spot to implement other WaitCommEvent flags
+     */
+        mask = async_get_eventmask(ov);
+        if(mask&EV_RXCHAR)
+            events |= POLLIN;
+        if(mask&EV_TXEMPTY)
+            events |= POLLOUT;
+        break;
+    }
+    return events;
+}
+
+/* receive a select event, and output a windows event */
+int serial_async_poll_event(struct object *obj, int event)
+{
+    int r=0;
+
+    /* 
+     * FIXME: here is the spot to implement other WaitCommEvent flags
+     */
+    if(event & POLLIN)
+        r |= EV_RXCHAR;
+    if(event & POLLOUT)
+        r |= EV_TXEMPTY;
+
+    return r;
+}
+
 /* create a serial */
 DECL_HANDLER(create_serial)
 {
diff --git a/server/trace.c b/server/trace.c
index 4ac36ea..c5ff2ea 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1455,6 +1455,27 @@
     fprintf( stderr, " commerror=%08x", req->commerror );
 }
 
+static void dump_create_async_request( const struct create_async_request *req )
+{
+    fprintf( stderr, " file_handle=%d,", req->file_handle );
+    fprintf( stderr, " overlapped=%p,", req->overlapped );
+    fprintf( stderr, " buffer=%p,", req->buffer );
+    fprintf( stderr, " count=%d,", req->count );
+    fprintf( stderr, " func=%p,", req->func );
+    fprintf( stderr, " type=%d", req->type );
+}
+
+static void dump_create_async_reply( const struct create_async_request *req )
+{
+    fprintf( stderr, " ov_handle=%d", req->ov_handle );
+}
+
+static void dump_async_result_request( const struct async_result_request *req )
+{
+    fprintf( stderr, " ov_handle=%d,", req->ov_handle );
+    fprintf( stderr, " result=%d", req->result );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_wait_process_request,
@@ -1564,6 +1585,8 @@
     (dump_func)dump_create_serial_request,
     (dump_func)dump_get_serial_info_request,
     (dump_func)dump_set_serial_info_request,
+    (dump_func)dump_create_async_request,
+    (dump_func)dump_async_result_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -1675,6 +1698,8 @@
     (dump_func)dump_create_serial_reply,
     (dump_func)dump_get_serial_info_reply,
     (dump_func)0,
+    (dump_func)dump_create_async_reply,
+    (dump_func)0,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -1786,6 +1811,8 @@
     "create_serial",
     "get_serial_info",
     "set_serial_info",
+    "create_async",
+    "async_result",
 };
 
 /* ### make_requests end ### */