mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-30 04:48:36 +00:00
ntdll: Move IOCTL_SERIAL_WAIT_ON_MASK to the server.
Tested with a serial pair with a null modem. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55947
This commit is contained in:
parent
05c0a50454
commit
35de72453c
|
@ -979,10 +979,8 @@ typedef struct serial_irq_info
|
||||||
*/
|
*/
|
||||||
typedef struct async_commio
|
typedef struct async_commio
|
||||||
{
|
{
|
||||||
HANDLE hDevice;
|
struct async_fileio io;
|
||||||
DWORD* events;
|
DWORD* events;
|
||||||
client_ptr_t iosb;
|
|
||||||
HANDLE hEvent;
|
|
||||||
UINT evtmask;
|
UINT evtmask;
|
||||||
UINT cookie;
|
UINT cookie;
|
||||||
UINT mstat;
|
UINT mstat;
|
||||||
|
@ -1083,84 +1081,87 @@ static DWORD check_events(int fd, UINT mask,
|
||||||
return ret & mask;
|
return ret & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
static BOOL async_wait_proc( void *user, ULONG_PTR *info, unsigned int *status )
|
||||||
* wait_for_event (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 void CALLBACK wait_for_event(LPVOID arg)
|
|
||||||
{
|
{
|
||||||
async_commio *commio = arg;
|
async_commio *commio = user;
|
||||||
int fd, needs_close;
|
int fd, needs_close;
|
||||||
|
|
||||||
if (!server_get_unix_fd( commio->hDevice, FILE_READ_DATA | FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))
|
if (*status != STATUS_ALERTED)
|
||||||
|
{
|
||||||
|
release_fileio( &commio->io );
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server_get_unix_fd( commio->io.handle, FILE_READ_DATA | FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))
|
||||||
{
|
{
|
||||||
serial_irq_info new_irq_info;
|
serial_irq_info new_irq_info;
|
||||||
UINT new_mstat, dummy, cookie;
|
UINT new_mstat, dummy, cookie;
|
||||||
LARGE_INTEGER time;
|
|
||||||
|
|
||||||
TRACE("device=%p fd=0x%08x mask=0x%08x buffer=%p event=%p irq_info=%p\n",
|
TRACE( "device=%p fd=0x%08x mask=0x%08x buffer=%p irq_info=%p\n",
|
||||||
commio->hDevice, fd, commio->evtmask, commio->events, commio->hEvent, &commio->irq_info);
|
commio->io.handle, fd, commio->evtmask, commio->events, &commio->irq_info );
|
||||||
|
|
||||||
time.QuadPart = (ULONGLONG)10000;
|
|
||||||
time.QuadPart = -time.QuadPart;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* TIOCMIWAIT is not adequate
|
|
||||||
*
|
|
||||||
* FIXME:
|
* FIXME:
|
||||||
* We don't handle the EV_RXFLAG (the eventchar)
|
* We don't handle the EV_RXFLAG (the eventchar)
|
||||||
*/
|
*/
|
||||||
NtDelayExecution(FALSE, &time);
|
|
||||||
get_irq_info(fd, &new_irq_info);
|
get_irq_info(fd, &new_irq_info);
|
||||||
if (get_modem_status(fd, &new_mstat))
|
if (get_modem_status(fd, &new_mstat))
|
||||||
{
|
{
|
||||||
TRACE("get_modem_status failed\n");
|
TRACE("get_modem_status failed\n");
|
||||||
*commio->events = 0;
|
*commio->events = 0;
|
||||||
break;
|
*status = STATUS_CANCELLED;
|
||||||
|
*info = 0;
|
||||||
}
|
}
|
||||||
*commio->events = check_events(fd, commio->evtmask,
|
else
|
||||||
|
{
|
||||||
|
DWORD events = check_events( fd, commio->evtmask,
|
||||||
&new_irq_info, &commio->irq_info,
|
&new_irq_info, &commio->irq_info,
|
||||||
new_mstat, commio->mstat, commio->pending_write);
|
new_mstat, commio->mstat, commio->pending_write );
|
||||||
if (*commio->events) break;
|
TRACE("events %#x\n", (int)events);
|
||||||
get_wait_mask(commio->hDevice, &dummy, &cookie, (commio->evtmask & EV_TXEMPTY) ? &commio->pending_write : NULL, FALSE);
|
if (events)
|
||||||
|
{
|
||||||
|
*commio->events = events;
|
||||||
|
*status = STATUS_SUCCESS;
|
||||||
|
*info = sizeof(events);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get_wait_mask( commio->io.handle, &dummy, &cookie, (commio->evtmask & EV_TXEMPTY) ? &commio->pending_write : NULL, FALSE );
|
||||||
if (commio->cookie != cookie)
|
if (commio->cookie != cookie)
|
||||||
{
|
{
|
||||||
*commio->events = 0;
|
*commio->events = 0;
|
||||||
break;
|
*status = STATUS_CANCELLED;
|
||||||
|
*info = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (needs_close) close( fd );
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (needs_close) close( fd );
|
if (needs_close) close( fd );
|
||||||
}
|
}
|
||||||
if (*commio->events) set_async_iosb( commio->iosb, STATUS_SUCCESS, sizeof(DWORD) );
|
stop_waiting( commio->io.handle );
|
||||||
else set_async_iosb( commio->iosb, STATUS_CANCELLED, 0 );
|
release_fileio( &commio->io );
|
||||||
stop_waiting(commio->hDevice);
|
return TRUE;
|
||||||
if (commio->hEvent) NtSetEvent(commio->hEvent, NULL);
|
|
||||||
free( commio );
|
|
||||||
NtTerminateThread( GetCurrentThread(), 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, client_ptr_t iosb_ptr, DWORD* events)
|
static NTSTATUS wait_on( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc,
|
||||||
|
void *apc_user, IO_STATUS_BLOCK *io, DWORD *out_buffer )
|
||||||
{
|
{
|
||||||
async_commio* commio;
|
async_commio* commio;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
HANDLE handle;
|
HANDLE wait_handle;
|
||||||
|
ULONG options;
|
||||||
|
|
||||||
if ((status = NtResetEvent(hEvent, NULL)))
|
if (!(commio = (async_commio *)alloc_fileio( sizeof(*commio), async_wait_proc, handle )))
|
||||||
return status;
|
return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
commio = malloc( sizeof(async_commio) );
|
commio->events = out_buffer;
|
||||||
if (!commio) return STATUS_NO_MEMORY;
|
|
||||||
|
|
||||||
commio->hDevice = hDevice;
|
|
||||||
commio->events = events;
|
|
||||||
commio->iosb = iosb_ptr;
|
|
||||||
commio->hEvent = hEvent;
|
|
||||||
commio->pending_write = 0;
|
commio->pending_write = 0;
|
||||||
status = get_wait_mask(commio->hDevice, &commio->evtmask, &commio->cookie, (commio->evtmask & EV_TXEMPTY) ? &commio->pending_write : NULL, TRUE);
|
status = get_wait_mask( handle, &commio->evtmask, &commio->cookie, (commio->evtmask & EV_TXEMPTY) ? &commio->pending_write : NULL, TRUE );
|
||||||
if (status)
|
if (status)
|
||||||
{
|
{
|
||||||
free( commio );
|
free( commio );
|
||||||
|
@ -1209,23 +1210,42 @@ static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, client_ptr_t iosb
|
||||||
(commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD)))
|
(commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD)))
|
||||||
goto out_now;
|
goto out_now;
|
||||||
|
|
||||||
|
SERVER_START_REQ( ioctl )
|
||||||
|
{
|
||||||
|
req->code = IOCTL_SERIAL_WAIT_ON_MASK;
|
||||||
|
req->async = server_async( handle, &commio->io, event, apc, apc_user, iosb_client_ptr(io) );
|
||||||
|
status = wine_server_call( req );
|
||||||
|
wait_handle = wine_server_ptr_handle( reply->wait );
|
||||||
|
options = reply->options;
|
||||||
|
}
|
||||||
|
SERVER_END_REQ;
|
||||||
|
|
||||||
|
if (status == STATUS_ALERTED)
|
||||||
|
{
|
||||||
/* We might have received something or the TX buffer is delivered */
|
/* We might have received something or the TX buffer is delivered */
|
||||||
*events = check_events(fd, commio->evtmask,
|
DWORD events = check_events(fd, commio->evtmask,
|
||||||
&commio->irq_info, &commio->irq_info,
|
&commio->irq_info, &commio->irq_info,
|
||||||
commio->mstat, commio->mstat, commio->pending_write);
|
commio->mstat, commio->mstat, commio->pending_write);
|
||||||
if (*events)
|
if (events)
|
||||||
{
|
{
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
goto out_now;
|
io->Status = STATUS_SUCCESS;
|
||||||
|
io->Information = sizeof(events);
|
||||||
|
*out_buffer = events;
|
||||||
|
set_async_direct_result( &wait_handle, STATUS_SUCCESS, sizeof(events), FALSE );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = STATUS_PENDING;
|
||||||
|
set_async_direct_result( &wait_handle, STATUS_PENDING, 0, TRUE );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create the worker thread for the task */
|
if (status != STATUS_PENDING)
|
||||||
/* FIXME: should use async I/O instead */
|
release_fileio( &commio->io );
|
||||||
status = NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(),
|
|
||||||
wait_for_event, commio, 0, 0, 0, 0, NULL );
|
if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
|
||||||
if (status != STATUS_SUCCESS) goto out_now;
|
return status;
|
||||||
NtClose( handle );
|
|
||||||
return STATUS_PENDING;
|
|
||||||
|
|
||||||
#if !defined(TIOCINQ) || (!(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)) || !defined(TIOCMGET) || !defined(TIOCM_CTS) ||!defined(TIOCM_DSR) || !defined(TIOCM_RNG) || !defined(TIOCM_CAR)
|
#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_caps:
|
error_caps:
|
||||||
|
@ -1233,7 +1253,6 @@ error_caps:
|
||||||
status = STATUS_INVALID_PARAMETER;
|
status = STATUS_INVALID_PARAMETER;
|
||||||
#endif
|
#endif
|
||||||
out_now:
|
out_now:
|
||||||
stop_waiting(commio->hDevice);
|
|
||||||
free( commio );
|
free( commio );
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1247,7 +1266,7 @@ static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, const char* ptr)
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS io_control( HANDLE device, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
|
NTSTATUS serial_DeviceIoControl( HANDLE device, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
|
||||||
IO_STATUS_BLOCK *io, UINT code, void *in_buffer,
|
IO_STATUS_BLOCK *io, UINT code, void *in_buffer,
|
||||||
UINT in_size, void *out_buffer, UINT out_size )
|
UINT in_size, void *out_buffer, UINT out_size )
|
||||||
{
|
{
|
||||||
|
@ -1447,11 +1466,14 @@ static NTSTATUS io_control( HANDLE device, HANDLE event, PIO_APC_ROUTINE apc, vo
|
||||||
case IOCTL_SERIAL_WAIT_ON_MASK:
|
case IOCTL_SERIAL_WAIT_ON_MASK:
|
||||||
if (out_buffer && out_size == sizeof(DWORD))
|
if (out_buffer && out_size == sizeof(DWORD))
|
||||||
{
|
{
|
||||||
if (!(status = wait_on(device, fd, event, iosb_client_ptr(io), out_buffer)))
|
status = wait_on( device, fd, event, apc, apc_user, io, out_buffer );
|
||||||
sz = sizeof(DWORD);
|
if (needs_close) close( fd );
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
status = STATUS_INVALID_PARAMETER;
|
status = STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n",
|
FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n",
|
||||||
|
@ -1464,54 +1486,7 @@ static NTSTATUS io_control( HANDLE device, HANDLE event, PIO_APC_ROUTINE apc, vo
|
||||||
error:
|
error:
|
||||||
io->Status = status;
|
io->Status = status;
|
||||||
io->Information = sz;
|
io->Information = sz;
|
||||||
if (event && status != STATUS_PENDING) NtSetEvent(event, NULL);
|
if (event) NtSetEvent(event, NULL);
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************
|
|
||||||
* serial_DeviceIoControl
|
|
||||||
*/
|
|
||||||
NTSTATUS serial_DeviceIoControl( HANDLE device, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
|
|
||||||
IO_STATUS_BLOCK *io, UINT code, void *in_buffer,
|
|
||||||
UINT in_size, void *out_buffer, UINT out_size )
|
|
||||||
{
|
|
||||||
NTSTATUS status;
|
|
||||||
|
|
||||||
if (code == IOCTL_SERIAL_WAIT_ON_MASK)
|
|
||||||
{
|
|
||||||
HANDLE hev = event;
|
|
||||||
|
|
||||||
/* this is an ioctl we implement in a non blocking way if event is not null
|
|
||||||
* so we have to explicitly wait if no event is provided
|
|
||||||
*/
|
|
||||||
if (!hev)
|
|
||||||
{
|
|
||||||
OBJECT_ATTRIBUTES attr;
|
|
||||||
|
|
||||||
attr.Length = sizeof(attr);
|
|
||||||
attr.RootDirectory = 0;
|
|
||||||
attr.ObjectName = NULL;
|
|
||||||
attr.Attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
|
|
||||||
attr.SecurityDescriptor = NULL;
|
|
||||||
attr.SecurityQualityOfService = NULL;
|
|
||||||
status = NtCreateEvent(&hev, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE);
|
|
||||||
|
|
||||||
if (status) return status;
|
|
||||||
}
|
|
||||||
status = io_control( device, hev, apc, apc_user, io, code,
|
|
||||||
in_buffer, in_size, out_buffer, out_size );
|
|
||||||
if (hev != event)
|
|
||||||
{
|
|
||||||
if (status == STATUS_PENDING)
|
|
||||||
{
|
|
||||||
NtWaitForSingleObject(hev, FALSE, NULL);
|
|
||||||
status = STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
NtClose(hev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else status = io_control( device, event, apc, apc_user, io, code,
|
|
||||||
in_buffer, in_size, out_buffer, out_size );
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,12 @@
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
|
|
||||||
|
struct wait_req
|
||||||
|
{
|
||||||
|
struct serial *serial;
|
||||||
|
struct timeout_user *timeout;
|
||||||
|
};
|
||||||
|
|
||||||
static void serial_dump( struct object *obj, int verbose );
|
static void serial_dump( struct object *obj, int verbose );
|
||||||
static struct fd *serial_get_fd( struct object *obj );
|
static struct fd *serial_get_fd( struct object *obj );
|
||||||
static void serial_destroy(struct object *obj);
|
static void serial_destroy(struct object *obj);
|
||||||
|
@ -65,6 +71,8 @@ struct serial
|
||||||
struct object obj;
|
struct object obj;
|
||||||
struct fd *fd;
|
struct fd *fd;
|
||||||
|
|
||||||
|
struct async_queue wait_q; /* queue for asynchronous WAIT_ON_MASK */
|
||||||
|
|
||||||
struct timeout_user *read_timer;
|
struct timeout_user *read_timer;
|
||||||
SERIAL_TIMEOUTS timeouts;
|
SERIAL_TIMEOUTS timeouts;
|
||||||
unsigned int eventmask;
|
unsigned int eventmask;
|
||||||
|
@ -138,6 +146,7 @@ struct object *create_serial( struct fd *fd )
|
||||||
serial->pending_write = 0;
|
serial->pending_write = 0;
|
||||||
serial->pending_wait = 0;
|
serial->pending_wait = 0;
|
||||||
memset( &serial->timeouts, 0, sizeof(serial->timeouts) );
|
memset( &serial->timeouts, 0, sizeof(serial->timeouts) );
|
||||||
|
init_async_queue( &serial->wait_q );
|
||||||
serial->fd = (struct fd *)grab_object( fd );
|
serial->fd = (struct fd *)grab_object( fd );
|
||||||
set_fd_user( fd, &serial_fd_ops, &serial->obj );
|
set_fd_user( fd, &serial_fd_ops, &serial->obj );
|
||||||
return &serial->obj;
|
return &serial->obj;
|
||||||
|
@ -153,6 +162,7 @@ static void serial_destroy( struct object *obj)
|
||||||
{
|
{
|
||||||
struct serial *serial = (struct serial *)obj;
|
struct serial *serial = (struct serial *)obj;
|
||||||
if (serial->read_timer) remove_timeout_user( serial->read_timer );
|
if (serial->read_timer) remove_timeout_user( serial->read_timer );
|
||||||
|
free_async_queue( &serial->wait_q );
|
||||||
release_object( serial->fd );
|
release_object( serial->fd );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +183,25 @@ static enum server_fd_type serial_get_fd_type( struct fd *fd )
|
||||||
return FD_TYPE_SERIAL;
|
return FD_TYPE_SERIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WAIT_ON_MASK_POLL_INTERVAL -10000
|
||||||
|
|
||||||
|
static void free_wait_req( void *private )
|
||||||
|
{
|
||||||
|
struct wait_req *req = private;
|
||||||
|
|
||||||
|
if (req->timeout) remove_timeout_user( req->timeout );
|
||||||
|
release_object( req->serial );
|
||||||
|
free( req );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void async_wait_timeout( void *private )
|
||||||
|
{
|
||||||
|
struct wait_req *req = private;
|
||||||
|
|
||||||
|
async_wake_up( &req->serial->wait_q, STATUS_ALERTED );
|
||||||
|
req->timeout = add_timeout_user( WAIT_ON_MASK_POLL_INTERVAL, async_wait_timeout, req );
|
||||||
|
}
|
||||||
|
|
||||||
static void serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
|
static void serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
|
||||||
{
|
{
|
||||||
struct serial *serial = get_fd_user( fd );
|
struct serial *serial = get_fd_user( fd );
|
||||||
|
@ -217,6 +246,26 @@ static void serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async
|
||||||
fd_async_wake_up( serial->fd, ASYNC_TYPE_WAIT, STATUS_SUCCESS );
|
fd_async_wake_up( serial->fd, ASYNC_TYPE_WAIT, STATUS_SUCCESS );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case IOCTL_SERIAL_WAIT_ON_MASK:
|
||||||
|
{
|
||||||
|
struct wait_req *req;
|
||||||
|
|
||||||
|
if (!(req = mem_alloc(sizeof(*req))))
|
||||||
|
return;
|
||||||
|
|
||||||
|
req->serial = (struct serial *)grab_object( serial );
|
||||||
|
if (!(req->timeout = add_timeout_user( WAIT_ON_MASK_POLL_INTERVAL, async_wait_timeout, req )))
|
||||||
|
{
|
||||||
|
free( req );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_set_completion_callback( async, free_wait_req, req );
|
||||||
|
queue_async( &serial->wait_q, async );
|
||||||
|
set_error( STATUS_ALERTED );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
set_error( STATUS_NOT_SUPPORTED );
|
set_error( STATUS_NOT_SUPPORTED );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue