*
* DEC 93 Erik Bos <erik@xs4all.nl>
* Copyright 1996 Marcus Meissner
- * Copyright 2005 Eric Pouech
+ * Copyright 2005,2006 Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#endif
#include <fcntl.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#include "wine/debug.h"
#ifdef HAVE_LINUX_SERIAL_H
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
#include <linux/serial.h>
#endif
+#if !defined(TIOCINQ) && defined(FIONREAD)
+#define TIOCINQ FIONREAD
+#endif
+
WINE_DEFAULT_DEBUG_CHANNEL(comm);
static const char* iocode2str(DWORD ioc)
{
switch (ioc)
{
-#define X(x) case (x): return #x;
+#define X(x) case (x): return #x
X(IOCTL_SERIAL_CLEAR_STATS);
X(IOCTL_SERIAL_CLR_DTR);
X(IOCTL_SERIAL_CLR_RTS);
X(IOCTL_SERIAL_WAIT_ON_MASK);
X(IOCTL_SERIAL_XOFF_COUNTER);
#undef X
- default: { static char tmp[32]; sprintf(tmp, "IOCTL_SERIAL_%ld\n", ioc); return tmp; }
+ default: { static char tmp[32]; sprintf(tmp, "IOCTL_SERIAL_%d\n", ioc); return tmp; }
}
}
ERR("tcgetattr error '%s'\n", strerror(errno));
return FILE_GetNtStatus();
}
-#ifndef __EMX__
-#ifdef CBAUD
- speed = port.c_cflag & CBAUD;
-#else
speed = cfgetospeed(&port);
-#endif
switch (speed)
{
case B0: sbr->BaudRate = 0; break;
ERR("unknown speed %x\n", speed);
return STATUS_INVALID_PARAMETER;
}
-#else
- return STATUS_INVALID_PARAMETER;
-#endif
return STATUS_SUCCESS;
}
static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf)
{
- int stat;
+ int stat = 0;
struct termios port;
-
+
if (tcgetattr(fd, &port) == -1)
{
ERR("tcgetattr error '%s'\n", strerror(errno));
return FILE_GetNtStatus();
}
+ /* termios does not support DTR/DSR flow control */
+ shf->ControlHandShake = 0;
+ shf->FlowReplace = 0;
#ifdef TIOCMGET
if (ioctl(fd, TIOCMGET, &stat) == -1)
{
WARN("ioctl error '%s'\n", strerror(errno));
- stat = DTR_CONTROL_ENABLE | RTS_CONTROL_ENABLE;
+ shf->ControlHandShake |= SERIAL_DTR_CONTROL;
+ shf->FlowReplace |= SERIAL_RTS_CONTROL;
}
+#else
+ WARN("Setting DTR/RTS to enabled by default\n");
+ shf->ControlHandShake |= SERIAL_DTR_CONTROL;
+ shf->FlowReplace |= SERIAL_RTS_CONTROL;
#endif
- /* termios does not support DTR/DSR flow control */
- shf->ControlHandShake = 0;
- shf->FlowReplace = 0;
#ifdef TIOCM_DTR
if (stat & TIOCM_DTR)
#endif
#ifdef CRTSCTS
if (port.c_cflag & CRTSCTS)
{
- shf->ControlHandShake |= SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE;
+ shf->FlowReplace |= SERIAL_RTS_CONTROL;
shf->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
}
else
#ifdef TIOCM_RTS
if (stat & TIOCM_RTS)
#endif
- shf->ControlHandShake |= SERIAL_RTS_CONTROL;
+ shf->FlowReplace |= SERIAL_RTS_CONTROL;
}
- if (port.c_iflag & IXON)
- shf->FlowReplace |= SERIAL_AUTO_RECEIVE;
if (port.c_iflag & IXOFF)
+ shf->FlowReplace |= SERIAL_AUTO_RECEIVE;
+ if (port.c_iflag & IXON)
shf->FlowReplace |= SERIAL_AUTO_TRANSMIT;
shf->XonLimit = 10;
#ifdef CMSPAR
case PARENB|CMSPAR: slc->Parity = MARKPARITY; break;
case PARENB|PARODD|CMSPAR: slc->Parity = SPACEPARITY; break;
- break;
#endif
}
switch (port.c_cflag & CSIZE)
case CS6: slc->WordLength = 6; break;
case CS7: slc->WordLength = 7; break;
case CS8: slc->WordLength = 8; break;
- default: ERR("unknown size %x\n", port.c_cflag & CSIZE);
+ default: ERR("unknown size %x\n", (UINT)(port.c_cflag & CSIZE));
}
-
+
if (port.c_cflag & CSTOPB)
{
if (slc->WordLength == 5)
static NTSTATUS get_modem_status(int fd, DWORD* lpModemStat)
{
- NTSTATUS status = STATUS_SUCCESS;
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
int mstat;
+ *lpModemStat = 0;
#ifdef TIOCMGET
- if (ioctl(fd, TIOCMGET, &mstat) == -1)
- {
- WARN("ioctl failed\n");
- status = FILE_GetNtStatus();
- }
- else
+ if (!ioctl(fd, TIOCMGET, &mstat))
{
- *lpModemStat = 0;
#ifdef TIOCM_CTS
if (mstat & TIOCM_CTS) *lpModemStat |= MS_CTS_ON;
#endif
(*lpModemStat & MS_RING_ON) ? "MS_RING_ON " : "",
(*lpModemStat & MS_DSR_ON) ? "MS_DSR_ON " : "",
(*lpModemStat & MS_CTS_ON) ? "MS_CTS_ON " : "");
+ return STATUS_SUCCESS;
}
-#else
- status = STATUS_NOT_SUPPORTED;
+ WARN("ioctl failed\n");
+ status = FILE_GetNtStatus();
#endif
return status;
}
return status;
}
+static NTSTATUS get_timeouts(HANDLE handle, SERIAL_TIMEOUTS* st)
+{
+ NTSTATUS status;
+ SERVER_START_REQ( get_serial_info )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ if (!(status = wine_server_call( req )))
+ {
+ st->ReadIntervalTimeout = reply->readinterval;
+ st->ReadTotalTimeoutMultiplier = reply->readmult;
+ st->ReadTotalTimeoutConstant = reply->readconst;
+ st->WriteTotalTimeoutMultiplier = reply->writemult;
+ st->WriteTotalTimeoutConstant = reply->writeconst;
+ }
+ }
+ SERVER_END_REQ;
+ return status;
+}
+
static NTSTATUS get_wait_mask(HANDLE hDevice, DWORD* mask)
{
NTSTATUS status;
SERVER_START_REQ( get_serial_info )
{
- req->handle = hDevice;
+ req->handle = wine_server_obj_handle( hDevice );
if (!(status = wine_server_call( req )))
*mask = reply->eventmask;
}
return FILE_GetNtStatus();
}
-#ifdef CBAUD
- port.c_cflag &= ~CBAUD;
switch (sbr->BaudRate)
{
- case 0: port.c_cflag |= B0; break;
- case 50: port.c_cflag |= B50; break;
- case 75: port.c_cflag |= B75; break;
- case 110:
- case CBR_110: port.c_cflag |= B110; break;
- case 134: port.c_cflag |= B134; break;
- case 150: port.c_cflag |= B150; break;
- case 200: port.c_cflag |= B200; break;
- case 300:
- case CBR_300: port.c_cflag |= B300; break;
+ case 0: cfsetospeed( &port, B0 ); break;
+ case 50: cfsetospeed( &port, B50 ); break;
+ case 75: cfsetospeed( &port, B75 ); break;
+ case 110:
+ case CBR_110: cfsetospeed( &port, B110 ); break;
+ case 134: cfsetospeed( &port, B134 ); break;
+ case 150: cfsetospeed( &port, B150 ); break;
+ case 200: cfsetospeed( &port, B200 ); break;
+ case 300:
+ case CBR_300: cfsetospeed( &port, B300 ); break;
case 600:
- case CBR_600: port.c_cflag |= B600; break;
+ case CBR_600: cfsetospeed( &port, B600 ); break;
case 1200:
- case CBR_1200: port.c_cflag |= B1200; break;
- case 1800: port.c_cflag |= B1800; break;
+ case CBR_1200: cfsetospeed( &port, B1200 ); break;
+ case 1800: cfsetospeed( &port, B1800 ); break;
case 2400:
- case CBR_2400: port.c_cflag |= B2400; break;
+ case CBR_2400: cfsetospeed( &port, B2400 ); break;
case 4800:
- case CBR_4800: port.c_cflag |= B4800; break;
+ case CBR_4800: cfsetospeed( &port, B4800 ); break;
case 9600:
- case CBR_9600: port.c_cflag |= B9600; break;
+ case CBR_9600: cfsetospeed( &port, B9600 ); break;
case 19200:
- case CBR_19200: port.c_cflag |= B19200; break;
+ case CBR_19200: cfsetospeed( &port, B19200 ); break;
case 38400:
- case CBR_38400: port.c_cflag |= B38400; break;
+ case CBR_38400: cfsetospeed( &port, B38400 ); break;
#ifdef B57600
- case 57600: port.c_cflag |= B57600; break;
+ case 57600: cfsetospeed( &port, B57600 ); break;
#endif
#ifdef B115200
- case 115200: port.c_cflag |= B115200;break;
+ case 115200: cfsetospeed( &port, B115200 ); break;
#endif
#ifdef B230400
- case 230400: port.c_cflag |= B230400;break;
+ case 230400: cfsetospeed( &port, B230400 ); break;
#endif
#ifdef B460800
- case 460800: port.c_cflag |= B460800;break;
+ case 460800: cfsetospeed( &port, B460800 ); break;
#endif
default:
#if defined (HAVE_LINUX_SERIAL_H) && defined (TIOCSSERIAL)
nuts.flags &= ~ASYNC_SPD_MASK;
nuts.flags |= ASYNC_SPD_CUST;
WARN("You (or a program acting at your behest) have specified\n"
- "a non-standard baud rate %ld. Wine will set the rate to %d,\n"
+ "a non-standard baud rate %d. Wine will set the rate to %d,\n"
"which is as close as we can get by our present understanding of your\n"
"hardware. I hope you know what you are doing. Any disruption Wine\n"
- "has caused to your linux system can be undone with setserial \n"
+ "has caused to your linux system can be undone with setserial\n"
"(see man setserial). If you have incapacitated a Hayes type modem,\n"
"reset it and it will probably recover.\n", sbr->BaudRate, arby);
ioctl(fd, TIOCSSERIAL, &nuts);
- port.c_cflag |= B38400;
+ cfsetospeed( &port, B38400 );
}
break;
-#endif /* Don't have linux/serial.h or lack TIOCSSERIAL */
- ERR("baudrate %ld\n", sbr->BaudRate);
- return STATUS_NOT_SUPPORTED;
- }
-#elif !defined(__EMX__)
- switch (sbr->BaudRate)
- {
- case 0: port.c_ospeed = B0; break;
- case 50: port.c_ospeed = B50; break;
- case 75: port.c_ospeed = B75; break;
- case 110:
- case CBR_110: port.c_ospeed = B110; break;
- case 134: port.c_ospeed = B134; break;
- case 150: port.c_ospeed = B150; break;
- case 200: port.c_ospeed = B200; break;
- case 300:
- case CBR_300: port.c_ospeed = B300; break;
- case 600:
- case CBR_600: port.c_ospeed = B600; break;
- case 1200:
- case CBR_1200: port.c_ospeed = B1200; break;
- case 1800: port.c_ospeed = B1800; break;
- case 2400:
- case CBR_2400: port.c_ospeed = B2400; break;
- case 4800:
- case CBR_4800: port.c_ospeed = B4800; break;
- case 9600:
- case CBR_9600: port.c_ospeed = B9600; break;
- case 19200:
- case CBR_19200: port.c_ospeed = B19200; break;
- case 38400:
- case CBR_38400: port.c_ospeed = B38400; break;
-#ifdef B57600
- case 57600:
- case CBR_57600: port.c_cflag |= B57600; break;
-#endif
-#ifdef B115200
- case 115200:
- case CBR_115200: port.c_cflag |= B115200;break;
-#endif
-#ifdef B230400
- case 230400: port.c_cflag |= B230400;break;
-#endif
-#ifdef B460800
- case 460800: port.c_cflag |= B460800;break;
-#endif
- default:
- ERR("baudrate %ld\n", sbr->BaudRate);
+#else /* Don't have linux/serial.h or lack TIOCSSERIAL */
+ ERR("baudrate %d\n", sbr->BaudRate);
return STATUS_NOT_SUPPORTED;
+#endif /* Don't have linux/serial.h or lack TIOCSSERIAL */
}
- port.c_ispeed = port.c_ospeed;
-#endif
+ cfsetispeed( &port, cfgetospeed(&port) );
if (tcsetattr(fd, TCSANOW, &port) == -1)
{
ERR("tcsetattr error '%s'\n", strerror(errno));
if (shf->ControlHandShake & SERIAL_DTR_HANDSHAKE)
{
WARN("DSR/DTR flow control not supported\n");
- } else if (shf->ControlHandShake & SERIAL_DTR_CONTROL)
+ } else if (!(shf->ControlHandShake & SERIAL_DTR_CONTROL))
whack_modem(fd, ~TIOCM_DTR, 0);
- else
+ else
whack_modem(fd, 0, TIOCM_DTR);
#endif
#ifdef TIOCM_RTS
- if (!(shf->ControlHandShake & SERIAL_DSR_HANDSHAKE))
+ if (!(shf->ControlHandShake & SERIAL_CTS_HANDSHAKE))
{
if ((shf->FlowReplace & (SERIAL_RTS_CONTROL|SERIAL_RTS_HANDSHAKE)) == 0)
whack_modem(fd, ~TIOCM_RTS, 0);
#endif
if (shf->FlowReplace & SERIAL_AUTO_RECEIVE)
- port.c_iflag |= IXON;
- else
- port.c_iflag &= ~IXON;
- if (shf->FlowReplace & SERIAL_AUTO_TRANSMIT)
port.c_iflag |= IXOFF;
else
port.c_iflag &= ~IXOFF;
+ if (shf->FlowReplace & SERIAL_AUTO_TRANSMIT)
+ port.c_iflag |= IXON;
+ else
+ port.c_iflag &= ~IXON;
if (tcsetattr(fd, TCSANOW, &port) == -1)
{
ERR("tcsetattr error '%s'\n", strerror(errno));
port.c_cflag &= ~(HUPCL);
port.c_cflag |= CLOCAL | CREAD;
- port.c_lflag &= ~(ICANON|ECHO|ISIG);
+ /*
+ * on FreeBSD, turning off ICANON does not disable IEXTEN,
+ * so we must turn it off explicitly. No harm done on Linux.
+ */
+ port.c_lflag &= ~(ICANON|ECHO|ISIG|IEXTEN);
port.c_lflag |= NOFLSH;
bytesize = slc->WordLength;
port.c_cflag &= ~(PARENB | PARODD);
#endif
+ /* make sure that reads don't block */
+ port.c_cc[VMIN] = 0;
+ port.c_cc[VTIME] = 0;
+
switch (slc->Parity)
{
case NOPARITY: port.c_iflag &= ~INPCK; break;
return STATUS_SUCCESS;
}
+static NTSTATUS set_queue_size(int fd, const SERIAL_QUEUE_SIZE* sqs)
+{
+ FIXME("insize %d outsize %d unimplemented stub\n", sqs->InSize, sqs->OutSize);
+ return STATUS_SUCCESS;
+}
+
static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc)
{
struct termios port;
return FILE_GetNtStatus();
}
- port.c_cc[VMIN ] = 0;
- port.c_cc[VTIME ] = 1;
-
port.c_cc[VEOF ] = sc->EofChar;
/* FIXME: sc->ErrorChar is not supported */
/* FIXME: sc->BreakChar is not supported */
return STATUS_SUCCESS;
}
+static NTSTATUS set_timeouts(HANDLE handle, const SERIAL_TIMEOUTS* st)
+{
+ NTSTATUS status;
+
+ SERVER_START_REQ( set_serial_info )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ req->flags = SERIALINFO_SET_TIMEOUTS;
+ req->readinterval = st->ReadIntervalTimeout ;
+ req->readmult = st->ReadTotalTimeoutMultiplier ;
+ req->readconst = st->ReadTotalTimeoutConstant ;
+ req->writemult = st->WriteTotalTimeoutMultiplier ;
+ req->writeconst = st->WriteTotalTimeoutConstant ;
+ status = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+ return status;
+}
+
static NTSTATUS set_wait_mask(HANDLE hDevice, DWORD mask)
{
NTSTATUS status;
SERVER_START_REQ( set_serial_info )
{
- req->handle = hDevice;
+ req->handle = wine_server_obj_handle( hDevice );
req->flags = SERIALINFO_SET_MASK;
req->eventmask = mask;
status = wine_server_call( req );
return status;
}
-static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, char* ptr)
+/*
+ * does not change IXOFF but simulates that IXOFF has been received:
+ */
+static NTSTATUS set_XOff(int fd)
+{
+ if (tcflow(fd, TCOOFF))
+ {
+ return FILE_GetNtStatus();
+ }
+ return STATUS_SUCCESS;
+}
+
+/*
+ * does not change IXON but simulates that IXON has been received:
+ */
+static NTSTATUS set_XOn(int fd)
+{
+ if (tcflow(fd, TCOON))
+ {
+ return FILE_GetNtStatus();
+ }
+ return STATUS_SUCCESS;
+}
+
+/* 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;
+ DWORD temt;
+}serial_irq_info;
+
+/***********************************************************************
+ * Data needed by the thread polling for the changing CommEvent
+ */
+typedef struct async_commio
+{
+ HANDLE hDevice;
+ DWORD* events;
+ IO_STATUS_BLOCK* iosb;
+ HANDLE hEvent;
+ DWORD evtmask;
+ DWORD mstat;
+ serial_irq_info irq_info;
+} async_commio;
+
+/***********************************************************************
+ * Get extended interrupt count info, needed for wait_on
+ */
+static NTSTATUS get_irq_info(int fd, serial_irq_info *irq_info)
+{
+#ifdef TIOCGICOUNT
+ struct serial_icounter_struct einfo;
+ if (!ioctl(fd, TIOCGICOUNT, &einfo))
+ {
+ 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;
+ }
+ else
+ {
+ TRACE("TIOCGICOUNT err %s\n", strerror(errno));
+ memset(irq_info,0, sizeof(serial_irq_info));
+ return FILE_GetNtStatus();
+ }
+#else
+ memset(irq_info,0, sizeof(serial_irq_info));
+ return STATUS_NOT_IMPLEMENTED;
+#endif
+ irq_info->temt = 0;
+ /* Generate a single TX_TXEMPTY event when the TX Buffer turns empty*/
+#ifdef TIOCSERGETLSR /* prefer to log the state TIOCSERGETLSR */
+ if (ioctl(fd, TIOCSERGETLSR, &irq_info->temt))
+ {
+ TRACE("TIOCSERGETLSR err %s\n", strerror(errno));
+ return FILE_GetNtStatus();
+ }
+#elif defined(TIOCOUTQ) /* otherwise we log when the out queue gets empty */
+ if (ioctl(fd, TIOCOUTQ, &irq_info->temt))
+ {
+ TRACE("TIOCOUTQ err %s\n", strerror(errno));
+ return FILE_GetNtStatus();
+ }
+ else
+ {
+ if (irq_info->temt == 0)
+ irq_info->temt = 1;
+ }
+#endif
+ return STATUS_SUCCESS;
+}
+
+
+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 ret = 0, queue;
+
+ TRACE("mask 0x%08x\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);
+
+ if (old->brk != new->brk) ret |= EV_BREAK;
+ if ((old_mstat & MS_CTS_ON ) != (new_mstat & MS_CTS_ON )) ret |= EV_CTS;
+ if ((old_mstat & MS_DSR_ON ) != (new_mstat & MS_DSR_ON )) ret |= EV_DSR;
+ if ((old_mstat & MS_RING_ON) != (new_mstat & MS_RING_ON)) ret |= EV_RING;
+ if ((old_mstat & MS_RLSD_ON) != (new_mstat & MS_RLSD_ON)) ret |= EV_RLSD;
+ if (old->frame != new->frame || old->overrun != new->overrun || old->parity != new->parity) ret |= EV_ERR;
+ 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)
+ {
+ if (!old->temt && new->temt)
+ ret |= EV_TXEMPTY;
+ }
+ return ret & mask;
+}
+
+/***********************************************************************
+ * 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 DWORD CALLBACK wait_for_event(LPVOID arg)
+{
+ async_commio *commio = arg;
+ int fd, needs_close;
+
+ if (!server_get_unix_fd( commio->hDevice, FILE_READ_DATA | FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))
+ {
+ serial_irq_info new_irq_info;
+ DWORD new_mstat, new_evtmask;
+ LARGE_INTEGER time;
+
+ TRACE("device=%p fd=0x%08x mask=0x%08x buffer=%p event=%p irq_info=%p\n",
+ commio->hDevice, fd, commio->evtmask, commio->events, commio->hEvent, &commio->irq_info);
+
+ time.QuadPart = (ULONGLONG)10000;
+ time.QuadPart = -time.QuadPart;
+ for (;;)
+ {
+ /*
+ * TIOCMIWAIT is not adequate
+ *
+ * FIXME:
+ * We don't handle the EV_RXFLAG (the eventchar)
+ */
+ NtDelayExecution(FALSE, &time);
+ get_irq_info(fd, &new_irq_info);
+ if (get_modem_status(fd, &new_mstat))
+ TRACE("get_modem_status failed\n");
+ *commio->events = check_events(fd, commio->evtmask,
+ &new_irq_info, &commio->irq_info,
+ new_mstat, commio->mstat);
+ if (*commio->events) break;
+ get_wait_mask(commio->hDevice, &new_evtmask);
+ if (commio->evtmask != new_evtmask)
+ {
+ *commio->events = 0;
+ break;
+ }
+ }
+ if (needs_close) close( fd );
+ }
+ if (commio->iosb) commio->iosb->u.Status = *commio->events ? STATUS_SUCCESS : STATUS_CANCELLED;
+ if (commio->hEvent) NtSetEvent(commio->hEvent, NULL);
+ RtlFreeHeap(GetProcessHeap(), 0, commio);
+ return 0;
+}
+
+static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, PIO_STATUS_BLOCK piosb, DWORD* events)
+{
+ async_commio* commio;
+ NTSTATUS status;
+
+ if ((status = NtResetEvent(hEvent, NULL)))
+ return status;
+
+ commio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof (async_commio));
+ if (!commio) return STATUS_NO_MEMORY;
+
+ commio->hDevice = hDevice;
+ commio->events = events;
+ commio->iosb = piosb;
+ commio->hEvent = hEvent;
+ get_wait_mask(commio->hDevice, &commio->evtmask);
+
+/* We may never return, if some capabilities miss
+ * Return error in that case
+ */
+#if !defined(TIOCINQ)
+ if (commio->evtmask & EV_RXCHAR)
+ goto error_caps;
+#endif
+#if !(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)
+ if (commio->evtmask & EV_TXEMPTY)
+ goto error_caps;
+#endif
+#if !defined(TIOCMGET)
+ if (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD))
+ goto error_caps;
+#endif
+#if !defined(TIOCM_CTS)
+ if (commio->evtmask & EV_CTS)
+ goto error_caps;
+#endif
+#if !defined(TIOCM_DSR)
+ if (commio->evtmask & EV_DSR)
+ goto error_caps;
+#endif
+#if !defined(TIOCM_RNG)
+ if (commio->evtmask & EV_RING)
+ goto error_caps;
+#endif
+#if !defined(TIOCM_CAR)
+ if (commio->evtmask & EV_RLSD)
+ goto error_caps;
+#endif
+ if (commio->evtmask & EV_RXFLAG)
+ FIXME("EV_RXFLAG not handled\n");
+
+ if ((status = get_irq_info(fd, &commio->irq_info)) &&
+ (commio->evtmask & (EV_BREAK | EV_ERR)))
+ goto out_now;
+
+ if ((status = get_modem_status(fd, &commio->mstat)) &&
+ (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD)))
+ goto out_now;
+
+ /* 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);
+ if (*events)
+ {
+ status = STATUS_SUCCESS;
+ goto out_now;
+ }
+
+ /* create the worker for the task */
+ status = RtlQueueWorkItem(wait_for_event, commio, 0 /* FIXME */);
+ if (status != STATUS_SUCCESS) goto out_now;
+ 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)
+error_caps:
+ FIXME("Returning error because of missing capabilities\n");
+ status = STATUS_INVALID_PARAMETER;
+#endif
+out_now:
+ RtlFreeHeap(GetProcessHeap(), 0, commio);
+ return status;
+}
+
+static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, const char* ptr)
{
/* FIXME: not perfect as it should bypass the in-queue */
WARN("(%p,'%c') not perfect!\n", hDevice, *ptr);
*
*
*/
-NTSTATUS COMM_DeviceIoControl(HANDLE hDevice,
- HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
- PVOID UserApcContext,
- PIO_STATUS_BLOCK piosb,
- ULONG dwIoControlCode,
- LPVOID lpInBuffer, DWORD nInBufferSize,
- LPVOID lpOutBuffer, DWORD nOutBufferSize)
+static inline NTSTATUS io_control(HANDLE hDevice,
+ HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
+ PVOID UserApcContext,
+ PIO_STATUS_BLOCK piosb,
+ ULONG dwIoControlCode,
+ LPVOID lpInBuffer, DWORD nInBufferSize,
+ LPVOID lpOutBuffer, DWORD nOutBufferSize)
{
DWORD sz = 0, access = FILE_READ_DATA;
NTSTATUS status = STATUS_SUCCESS;
- int fd;
+ int fd = -1, needs_close = 0;
- TRACE("%p %s %p %ld %p %ld %p\n",
+ TRACE("%p %s %p %d %p %d %p\n",
hDevice, iocode2str(dwIoControlCode), lpInBuffer, nInBufferSize,
lpOutBuffer, nOutBufferSize, piosb);
piosb->Information = 0;
- if ((status = wine_server_handle_to_fd( hDevice, access, &fd, NULL ))) goto error;
+ if (dwIoControlCode != IOCTL_SERIAL_GET_TIMEOUTS &&
+ dwIoControlCode != IOCTL_SERIAL_SET_TIMEOUTS)
+ {
+ enum server_fd_type type;
+ if ((status = server_get_unix_fd( hDevice, access, &fd, &needs_close, &type, NULL )))
+ goto error;
+ if (type != FD_TYPE_SERIAL)
+ {
+ if (needs_close) close( fd );
+ status = STATUS_OBJECT_TYPE_MISMATCH;
+ goto error;
+ }
+ }
switch (dwIoControlCode)
{
+ case IOCTL_SERIAL_CLR_DTR:
+#ifdef TIOCM_DTR
+ if (whack_modem(fd, ~TIOCM_DTR, 0) == -1) status = FILE_GetNtStatus();
+#else
+ status = STATUS_NOT_SUPPORTED;
+#endif
+ break;
+ case IOCTL_SERIAL_CLR_RTS:
+#ifdef TIOCM_RTS
+ if (whack_modem(fd, ~TIOCM_RTS, 0) == -1) status = FILE_GetNtStatus();
+#else
+ status = STATUS_NOT_SUPPORTED;
+#endif
+ break;
case IOCTL_SERIAL_GET_BAUD_RATE:
if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_BAUD_RATE))
{
- if (!(status = get_baud_rate(fd, (SERIAL_BAUD_RATE*)lpOutBuffer)))
+ if (!(status = get_baud_rate(fd, lpOutBuffer)))
sz = sizeof(SERIAL_BAUD_RATE);
}
else
case IOCTL_SERIAL_GET_CHARS:
if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_CHARS))
{
- if (!(status = get_special_chars(fd, (SERIAL_CHARS*)lpOutBuffer)))
+ if (!(status = get_special_chars(fd, lpOutBuffer)))
sz = sizeof(SERIAL_CHARS);
}
else
case IOCTL_SERIAL_GET_COMMSTATUS:
if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_STATUS))
{
- if (!(status = get_status(fd, (SERIAL_STATUS*)lpOutBuffer)))
+ if (!(status = get_status(fd, lpOutBuffer)))
sz = sizeof(SERIAL_STATUS);
}
else status = STATUS_INVALID_PARAMETER;
case IOCTL_SERIAL_GET_HANDFLOW:
if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_HANDFLOW))
{
- if (!(status = get_hand_flow(fd, (SERIAL_HANDFLOW*)lpOutBuffer)))
+ if (!(status = get_hand_flow(fd, lpOutBuffer)))
sz = sizeof(SERIAL_HANDFLOW);
}
else
case IOCTL_SERIAL_GET_LINE_CONTROL:
if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_LINE_CONTROL))
{
- if (!(status = get_line_control(fd, (SERIAL_LINE_CONTROL*)lpOutBuffer)))
+ if (!(status = get_line_control(fd, lpOutBuffer)))
sz = sizeof(SERIAL_LINE_CONTROL);
}
else
case IOCTL_SERIAL_GET_MODEMSTATUS:
if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
{
- if (!(status = get_modem_status(fd, (DWORD*)lpOutBuffer)))
+ if (!(status = get_modem_status(fd, lpOutBuffer)))
sz = sizeof(DWORD);
}
else status = STATUS_INVALID_PARAMETER;
break;
+ case IOCTL_SERIAL_GET_TIMEOUTS:
+ if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_TIMEOUTS))
+ {
+ if (!(status = get_timeouts(hDevice, lpOutBuffer)))
+ sz = sizeof(SERIAL_TIMEOUTS);
+ }
+ else
+ status = STATUS_INVALID_PARAMETER;
+ break;
case IOCTL_SERIAL_GET_WAIT_MASK:
if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
{
- if (!(status = get_wait_mask(hDevice, (DWORD*)lpOutBuffer)))
+ if (!(status = get_wait_mask(hDevice, lpOutBuffer)))
sz = sizeof(DWORD);
}
else
else
status = STATUS_INVALID_PARAMETER;
break;
+ case IOCTL_SERIAL_RESET_DEVICE:
+ FIXME("Unsupported\n");
+ break;
case IOCTL_SERIAL_SET_BAUD_RATE:
if (lpInBuffer && nInBufferSize == sizeof(SERIAL_BAUD_RATE))
- status = set_baud_rate(fd, (const SERIAL_BAUD_RATE*)lpInBuffer);
+ status = set_baud_rate(fd, lpInBuffer);
else
status = STATUS_INVALID_PARAMETER;
break;
break;
case IOCTL_SERIAL_SET_CHARS:
if (lpInBuffer && nInBufferSize == sizeof(SERIAL_CHARS))
- status = set_special_chars(fd, (const SERIAL_CHARS*)lpInBuffer);
+ status = set_special_chars(fd, lpInBuffer);
else
status = STATUS_INVALID_PARAMETER;
break;
+ case IOCTL_SERIAL_SET_DTR:
+#ifdef TIOCM_DTR
+ if (whack_modem(fd, 0, TIOCM_DTR) == -1) status = FILE_GetNtStatus();
+#else
+ status = STATUS_NOT_SUPPORTED;
+#endif
+ break;
case IOCTL_SERIAL_SET_HANDFLOW:
if (lpInBuffer && nInBufferSize == sizeof(SERIAL_HANDFLOW))
- status = set_handflow(fd, (const SERIAL_HANDFLOW*)lpInBuffer);
+ status = set_handflow(fd, lpInBuffer);
else
status = STATUS_INVALID_PARAMETER;
break;
case IOCTL_SERIAL_SET_LINE_CONTROL:
if (lpInBuffer && nInBufferSize == sizeof(SERIAL_LINE_CONTROL))
- status = set_line_control(fd, (const SERIAL_LINE_CONTROL*)lpInBuffer);
+ status = set_line_control(fd, lpInBuffer);
+ else
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ case IOCTL_SERIAL_SET_QUEUE_SIZE:
+ if (lpInBuffer && nInBufferSize == sizeof(SERIAL_QUEUE_SIZE))
+ status = set_queue_size(fd, lpInBuffer);
+ else
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ case IOCTL_SERIAL_SET_RTS:
+#ifdef TIOCM_RTS
+ if (whack_modem(fd, 0, TIOCM_RTS) == -1) status = FILE_GetNtStatus();
+#else
+ status = STATUS_NOT_SUPPORTED;
+#endif
+ break;
+ case IOCTL_SERIAL_SET_TIMEOUTS:
+ if (lpInBuffer && nInBufferSize == sizeof(SERIAL_TIMEOUTS))
+ status = set_timeouts(hDevice, lpInBuffer);
else
status = STATUS_INVALID_PARAMETER;
break;
}
else status = STATUS_INVALID_PARAMETER;
break;
+ case IOCTL_SERIAL_SET_XOFF:
+ status = set_XOff(fd);
+ break;
+ case IOCTL_SERIAL_SET_XON:
+ status = set_XOn(fd);
+ break;
+ case IOCTL_SERIAL_WAIT_ON_MASK:
+ if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
+ {
+ if (!(status = wait_on(hDevice, fd, hEvent, piosb, lpOutBuffer)))
+ sz = sizeof(DWORD);
+ }
+ else
+ status = STATUS_INVALID_PARAMETER;
+ break;
default:
- FIXME("Unsupported IOCTL %lx (type=%lx access=%lx func=%lx meth=%lx)\n",
+ FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n",
dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
(dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
sz = 0;
status = STATUS_INVALID_PARAMETER;
break;
}
- wine_server_release_fd( hDevice, fd );
+ if (needs_close) close( fd );
error:
piosb->u.Status = status;
piosb->Information = sz;
- if (hEvent) NtSetEvent(hEvent, NULL);
+ if (hEvent && status != STATUS_PENDING) NtSetEvent(hEvent, NULL);
+ return status;
+}
+
+NTSTATUS COMM_DeviceIoControl(HANDLE hDevice,
+ HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
+ PVOID UserApcContext,
+ PIO_STATUS_BLOCK piosb,
+ ULONG dwIoControlCode,
+ LPVOID lpInBuffer, DWORD nInBufferSize,
+ LPVOID lpOutBuffer, DWORD nOutBufferSize)
+{
+ NTSTATUS status;
+
+ if (dwIoControlCode == IOCTL_SERIAL_WAIT_ON_MASK)
+ {
+ HANDLE hev = hEvent;
+
+ /* this is an ioctl we implement in a non blocking way if hEvent is not
+ * null
+ * so we have to explicitly wait if no hEvent 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) goto done;
+ }
+ status = io_control(hDevice, hev, UserApcRoutine, UserApcContext,
+ piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
+ lpOutBuffer, nOutBufferSize);
+ if (hev != hEvent)
+ {
+ if (status == STATUS_PENDING)
+ {
+ NtWaitForSingleObject(hev, FALSE, NULL);
+ status = STATUS_SUCCESS;
+ }
+ NtClose(hev);
+ }
+ }
+ else status = io_control(hDevice, hEvent, UserApcRoutine, UserApcContext,
+ piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
+ lpOutBuffer, nOutBufferSize);
+done:
return status;
}