271 lines
6.8 KiB
C
271 lines
6.8 KiB
C
|
/*
|
||
|
* 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 );
|
||
|
}
|
||
|
}
|
||
|
|