diff options
author | Glenn Morris <rgm@gnu.org> | 2008-06-13 08:08:20 +0000 |
---|---|---|
committer | Glenn Morris <rgm@gnu.org> | 2008-06-13 08:08:20 +0000 |
commit | d888760c951787ccc3a48d7f90bc798ee0996b9b (patch) | |
tree | 1aabac1ac803d6d2e8614eef3dcf35874e5841de /src/w32.c | |
parent | 07d99e7544ffa51339ae1ba1a3785f49247e0c6e (diff) | |
download | emacs-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.c | 315 |
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 |