summaryrefslogtreecommitdiff
path: root/src/w32.c
diff options
context:
space:
mode:
authorGlenn Morris <rgm@gnu.org>2008-06-13 08:08:20 +0000
committerGlenn Morris <rgm@gnu.org>2008-06-13 08:08:20 +0000
commitd888760c951787ccc3a48d7f90bc798ee0996b9b (patch)
tree1aabac1ac803d6d2e8614eef3dcf35874e5841de /src/w32.c
parent07d99e7544ffa51339ae1ba1a3785f49247e0c6e (diff)
downloademacs-d888760c951787ccc3a48d7f90bc798ee0996b9b.tar.gz
Daniel Engeler <engeler at gmail.com>
These changes add serial port access. * process.c: Add HAVE_SERIAL. (Fdelete_process, Fprocess_status, Fset_process_buffer) (Fset_process_filter, Fset_process_sentinel, Fprocess_contact) (list_processes_1, select_wrapper, Fstop_process) (Fcontinue_process, Fprocess_send_eof, kill_buffer_processes) (status_notify): Modify to handle serial processes. [HAVE_SERIAL] (Fserial_process_configure) [HAVE_SERIAL] (make_serial_process_unwind, Fmake_serial_process): New functions. * process.h (struct Lisp_Process): Add `type'. * sysdep.c [HAVE_TERMIOS] (serial_open, serial_configure): New functions. * w32.c (_sys_read_ahead, sys_read, sys_write): Modify to handle serial ports. (serial_open, serial_configure) New functions. * w32.h: Add FILE_SERIAL. (struct _child_process): Add ovl_read, ovl_write.
Diffstat (limited to 'src/w32.c')
-rw-r--r--src/w32.c315
1 files changed, 310 insertions, 5 deletions
diff --git a/src/w32.c b/src/w32.c
index 81accb6b610..e0e19c3508a 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -102,6 +102,13 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "systime.h"
#include "dispextern.h" /* for xstrcasecmp */
+/* For serial_configure() and serial_open() */
+#include "process.h"
+/* From process.c */
+extern Lisp_Object QCport, QCspeed, QCprocess;
+extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven;
+extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary;
+
typedef HRESULT (WINAPI * ShGetFolderPath_fn)
(IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
@@ -4063,10 +4070,10 @@ _sys_read_ahead (int fd)
if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
return STATUS_READ_ERROR;
- if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0
+ if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
|| (fd_info[fd].flags & FILE_READ) == 0)
{
- DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd));
+ DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
abort ();
}
@@ -4080,7 +4087,7 @@ _sys_read_ahead (int fd)
reporting that input is available; we need this because Windows 95
connects DOS programs to pipes by making the pipe appear to be
the normal console stdout - as a result most DOS programs will
- write to stdout without buffering, ie. one character at a
+ write to stdout without buffering, ie. one character at a
time. Even some W32 programs do this - "dir" in a command
shell on NT is very slow if we don't do this. */
if (rc > 0)
@@ -4096,6 +4103,29 @@ _sys_read_ahead (int fd)
Sleep (0);
}
}
+ else if (fd_info[fd].flags & FILE_SERIAL)
+ {
+ HANDLE hnd = fd_info[fd].hnd;
+ OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
+ COMMTIMEOUTS ct;
+
+ /* Configure timeouts for blocking read. */
+ if (!GetCommTimeouts (hnd, &ct))
+ return STATUS_READ_ERROR;
+ ct.ReadIntervalTimeout = 0;
+ ct.ReadTotalTimeoutMultiplier = 0;
+ ct.ReadTotalTimeoutConstant = 0;
+ if (!SetCommTimeouts (hnd, &ct))
+ return STATUS_READ_ERROR;
+
+ if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
+ {
+ if (GetLastError () != ERROR_IO_PENDING)
+ return STATUS_READ_ERROR;
+ if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
+ return STATUS_READ_ERROR;
+ }
+ }
#ifdef HAVE_SOCKETS
else if (fd_info[fd].flags & FILE_SOCKET)
{
@@ -4167,7 +4197,7 @@ sys_read (int fd, char * buffer, unsigned int count)
return -1;
}
- if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+ if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
{
child_process *cp = fd_info[fd].cp;
@@ -4238,6 +4268,52 @@ sys_read (int fd, char * buffer, unsigned int count)
if (to_read > 0)
nchars += _read (fd, buffer, to_read);
}
+ else if (fd_info[fd].flags & FILE_SERIAL)
+ {
+ HANDLE hnd = fd_info[fd].hnd;
+ OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
+ DWORD err = 0;
+ int rc = 0;
+ COMMTIMEOUTS ct;
+
+ if (count > 0)
+ {
+ /* Configure timeouts for non-blocking read. */
+ if (!GetCommTimeouts (hnd, &ct))
+ {
+ errno = EIO;
+ return -1;
+ }
+ ct.ReadIntervalTimeout = MAXDWORD;
+ ct.ReadTotalTimeoutMultiplier = 0;
+ ct.ReadTotalTimeoutConstant = 0;
+ if (!SetCommTimeouts (hnd, &ct))
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ if (!ResetEvent (ovl->hEvent))
+ {
+ errno = EIO;
+ return -1;
+ }
+ if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
+ {
+ if (GetLastError () != ERROR_IO_PENDING)
+ {
+ errno = EIO;
+ return -1;
+ }
+ if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
+ {
+ errno = EIO;
+ return -1;
+ }
+ }
+ nchars += rc;
+ }
+ }
#ifdef HAVE_SOCKETS
else /* FILE_SOCKET */
{
@@ -4299,6 +4375,9 @@ sys_read (int fd, char * buffer, unsigned int count)
return nchars;
}
+/* From w32xfns.c */
+extern HANDLE interrupt_handle;
+
/* For now, don't bother with a non-blocking mode */
int
sys_write (int fd, const void * buffer, unsigned int count)
@@ -4311,7 +4390,7 @@ sys_write (int fd, const void * buffer, unsigned int count)
return -1;
}
- if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+ if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
{
if ((fd_info[fd].flags & FILE_WRITE) == 0)
{
@@ -4352,6 +4431,42 @@ sys_write (int fd, const void * buffer, unsigned int count)
}
}
+ if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
+ {
+ HANDLE hnd = (HANDLE) _get_osfhandle (fd);
+ OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
+ HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
+ DWORD active = 0;
+
+ if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
+ {
+ if (GetLastError () != ERROR_IO_PENDING)
+ {
+ errno = EIO;
+ return -1;
+ }
+ if (detect_input_pending ())
+ active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
+ QS_ALLINPUT);
+ else
+ active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
+ if (active == WAIT_OBJECT_0)
+ { /* User pressed C-g, cancel write, then leave. Don't bother
+ cleaning up as we may only get stuck in buggy drivers. */
+ PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
+ CancelIo (hnd);
+ errno = EIO;
+ return -1;
+ }
+ if (active == WAIT_OBJECT_0 + 1
+ && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
+ {
+ errno = EIO;
+ return -1;
+ }
+ }
+ }
+ else
#ifdef HAVE_SOCKETS
if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
{
@@ -4612,6 +4727,196 @@ globals_of_w32 ()
strcpy (dflt_group_name, "None");
}
+/* For make-serial-process */
+int serial_open (char *port)
+{
+ HANDLE hnd;
+ child_process *cp;
+ int fd = -1;
+
+ hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (hnd == INVALID_HANDLE_VALUE)
+ error ("Could not open %s", port);
+ fd = (int) _open_osfhandle ((int) hnd, 0);
+ if (fd == -1)
+ error ("Could not open %s", port);
+
+ cp = new_child ();
+ if (!cp)
+ error ("Could not create child process");
+ cp->fd = fd;
+ cp->status = STATUS_READ_ACKNOWLEDGED;
+ fd_info[ fd ].hnd = hnd;
+ fd_info[ fd ].flags |=
+ FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
+ if (fd_info[ fd ].cp != NULL)
+ {
+ error ("fd_info[fd = %d] is already in use", fd);
+ }
+ fd_info[ fd ].cp = cp;
+ cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (cp->ovl_read.hEvent == NULL)
+ error ("Could not create read event");
+ cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (cp->ovl_write.hEvent == NULL)
+ error ("Could not create write event");
+
+ return fd;
+}
+
+/* For serial-process-configure */
+void
+serial_configure (struct Lisp_Process *p,
+ Lisp_Object contact)
+{
+ Lisp_Object childp2 = Qnil;
+ Lisp_Object tem = Qnil;
+ HANDLE hnd;
+ DCB dcb;
+ COMMTIMEOUTS ct;
+ char summary[4] = "???"; /* This usually becomes "8N1". */
+
+ if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
+ error ("Not a serial process");
+ hnd = fd_info[ p->outfd ].hnd;
+
+ childp2 = Fcopy_sequence (p->childp);
+
+ /* Initialize timeouts for blocking read and blocking write. */
+ if (!GetCommTimeouts (hnd, &ct))
+ error ("GetCommTimeouts() failed");
+ ct.ReadIntervalTimeout = 0;
+ ct.ReadTotalTimeoutMultiplier = 0;
+ ct.ReadTotalTimeoutConstant = 0;
+ ct.WriteTotalTimeoutMultiplier = 0;
+ ct.WriteTotalTimeoutConstant = 0;
+ if (!SetCommTimeouts (hnd, &ct))
+ error ("SetCommTimeouts() failed");
+ /* Read port attributes and prepare default configuration. */
+ memset (&dcb, 0, sizeof (dcb));
+ dcb.DCBlength = sizeof (DCB);
+ if (!GetCommState (hnd, &dcb))
+ error ("GetCommState() failed");
+ dcb.fBinary = TRUE;
+ dcb.fNull = FALSE;
+ dcb.fAbortOnError = FALSE;
+ /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
+ dcb.ErrorChar = 0;
+ dcb.EofChar = 0;
+ dcb.EvtChar = 0;
+
+ /* Configure speed. */
+ if (!NILP (Fplist_member (contact, QCspeed)))
+ tem = Fplist_get (contact, QCspeed);
+ else
+ tem = Fplist_get (p->childp, QCspeed);
+ CHECK_NUMBER (tem);
+ dcb.BaudRate = XINT (tem);
+ childp2 = Fplist_put (childp2, QCspeed, tem);
+
+ /* Configure bytesize. */
+ if (!NILP (Fplist_member (contact, QCbytesize)))
+ tem = Fplist_get (contact, QCbytesize);
+ else
+ tem = Fplist_get (p->childp, QCbytesize);
+ if (NILP (tem))
+ tem = make_number (8);
+ CHECK_NUMBER (tem);
+ if (XINT (tem) != 7 && XINT (tem) != 8)
+ error (":bytesize must be nil (8), 7, or 8");
+ dcb.ByteSize = XINT (tem);
+ summary[0] = XINT (tem) + '0';
+ childp2 = Fplist_put (childp2, QCbytesize, tem);
+
+ /* Configure parity. */
+ if (!NILP (Fplist_member (contact, QCparity)))
+ tem = Fplist_get (contact, QCparity);
+ else
+ tem = Fplist_get (p->childp, QCparity);
+ if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
+ error (":parity must be nil (no parity), `even', or `odd'");
+ dcb.fParity = FALSE;
+ dcb.Parity = NOPARITY;
+ dcb.fErrorChar = FALSE;
+ if (NILP (tem))
+ {
+ summary[1] = 'N';
+ }
+ else if (EQ (tem, Qeven))
+ {
+ summary[1] = 'E';
+ dcb.fParity = TRUE;
+ dcb.Parity = EVENPARITY;
+ dcb.fErrorChar = TRUE;
+ }
+ else if (EQ (tem, Qodd))
+ {
+ summary[1] = 'O';
+ dcb.fParity = TRUE;
+ dcb.Parity = ODDPARITY;
+ dcb.fErrorChar = TRUE;
+ }
+ childp2 = Fplist_put (childp2, QCparity, tem);
+
+ /* Configure stopbits. */
+ if (!NILP (Fplist_member (contact, QCstopbits)))
+ tem = Fplist_get (contact, QCstopbits);
+ else
+ tem = Fplist_get (p->childp, QCstopbits);
+ if (NILP (tem))
+ tem = make_number (1);
+ CHECK_NUMBER (tem);
+ if (XINT (tem) != 1 && XINT (tem) != 2)
+ error (":stopbits must be nil (1 stopbit), 1, or 2");
+ summary[2] = XINT (tem) + '0';
+ if (XINT (tem) == 1)
+ dcb.StopBits = ONESTOPBIT;
+ else if (XINT (tem) == 2)
+ dcb.StopBits = TWOSTOPBITS;
+ childp2 = Fplist_put (childp2, QCstopbits, tem);
+
+ /* Configure flowcontrol. */
+ if (!NILP (Fplist_member (contact, QCflowcontrol)))
+ tem = Fplist_get (contact, QCflowcontrol);
+ else
+ tem = Fplist_get (p->childp, QCflowcontrol);
+ if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
+ error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
+ dcb.fOutxCtsFlow = FALSE;
+ dcb.fOutxDsrFlow = FALSE;
+ dcb.fDtrControl = DTR_CONTROL_DISABLE;
+ dcb.fDsrSensitivity = FALSE;
+ dcb.fTXContinueOnXoff = FALSE;
+ dcb.fOutX = FALSE;
+ dcb.fInX = FALSE;
+ dcb.fRtsControl = RTS_CONTROL_DISABLE;
+ dcb.XonChar = 17; /* Control-Q */
+ dcb.XoffChar = 19; /* Control-S */
+ if (NILP (tem))
+ {
+ /* Already configured. */
+ }
+ else if (EQ (tem, Qhw))
+ {
+ dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ dcb.fOutxCtsFlow = TRUE;
+ }
+ else if (EQ (tem, Qsw))
+ {
+ dcb.fOutX = TRUE;
+ dcb.fInX = TRUE;
+ }
+ childp2 = Fplist_put (childp2, QCflowcontrol, tem);
+
+ /* Activate configuration. */
+ if (!SetCommState (hnd, &dcb))
+ error ("SetCommState() failed");
+
+ childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
+ p->childp = childp2;
+}
+
/* end of w32.c */
/* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1