📄 fhandler_serial.cc
字号:
/* fhandler_serial.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.This file is part of Cygwin.This software is a copyrighted work licensed under the terms of theCygwin license. Please consult the file "CYGWIN_LICENSE" fordetails. */#include "winsup.h"#include <errno.h>#include <unistd.h>#include <stdlib.h>#include "cygerrno.h"#include "security.h"#include "fhandler.h"#include "sigproc.h"#include "pinfo.h"#include <sys/termios.h>#include <ddk/ntddser.h>/**********************************************************************//* fhandler_serial */fhandler_serial::fhandler_serial (int unit) : fhandler_base (FH_SERIAL, unit), vmin_ (0), vtime_ (0), pgrp_ (myself->pgid){ set_need_fork_fixup ();}voidfhandler_serial::overlapped_setup (){ memset (&io_status, 0, sizeof (io_status)); io_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); ProtectHandle (io_status.hEvent); overlapped_armed = 0;}intfhandler_serial::raw_read (void *ptr, size_t ulen){ int tot; DWORD n; HANDLE w4[2]; size_t minchars = vmin_ ?: ulen; w4[0] = io_status.hEvent; w4[1] = signal_arrived; debug_printf ("ulen %d, vmin_ %d, vtime_ %d, hEvent %p", ulen, vmin_, vtime_, io_status.hEvent); if (!overlapped_armed) { (void) SetCommMask (get_handle (), EV_RXCHAR); ResetEvent (io_status.hEvent); } for (n = 0, tot = 0; ulen; ulen -= n, ptr = (char *) ptr + n) { COMSTAT st; DWORD inq = 1; n = 0; if (!vtime_ && !vmin_) inq = ulen; else if (vtime_) { inq = ulen; // non-interruptible -- have to use kernel timeouts // also note that this is not strictly correct. // if vmin > ulen then things won't work right. overlapped_armed = -1; } if (!ClearCommError (get_handle (), &ev, &st)) goto err; else if (ev) termios_printf ("error detected %x", ev); else if (st.cbInQue) inq = st.cbInQue; else if (!overlapped_armed) { if ((size_t) tot >= minchars) break; else if (WaitCommEvent (get_handle (), &ev, &io_status)) { debug_printf ("WaitCommEvent succeeded: ev %x", ev); if (!ev) continue; } else if (GetLastError () != ERROR_IO_PENDING) goto err; else { overlapped_armed = 1; switch (WaitForMultipleObjects (2, w4, FALSE, INFINITE)) { case WAIT_OBJECT_0: if (!GetOverlappedResult (get_handle (), &io_status, &n, FALSE)) goto err; debug_printf ("n %d, ev %x", n, ev); break; case WAIT_OBJECT_0 + 1: tot = -1; PurgeComm (get_handle (), PURGE_RXABORT); overlapped_armed = 0; set_sig_errno (EINTR); goto out; default: goto err; } } } overlapped_armed = 0; ResetEvent (io_status.hEvent); if (inq > ulen) inq = ulen; debug_printf ("inq %d", inq); if (ReadFile (get_handle (), ptr, min (inq, ulen), &n, &io_status)) /* Got something */; else if (GetLastError () != ERROR_IO_PENDING) goto err; else if (!GetOverlappedResult (get_handle (), &io_status, &n, TRUE)) goto err; tot += n; debug_printf ("vtime_ %d, vmin_ %d, n %d, tot %d", vtime_, vmin_, n, tot); if (vtime_ || !vmin_ || !n) break; continue; err: PurgeComm (get_handle (), PURGE_RXABORT); debug_printf ("err %E"); if (GetLastError () == ERROR_OPERATION_ABORTED) n = 0; else { tot = -1; __seterrno (); break; } }out: return tot;}/* Cover function to WriteFile to provide Posix interface and semantics (as much as possible). */intfhandler_serial::raw_write (const void *ptr, size_t len){ DWORD bytes_written; OVERLAPPED write_status; memset (&write_status, 0, sizeof (write_status)); write_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); ProtectHandle (write_status.hEvent); for (;;) { if (WriteFile (get_handle (), ptr, len, &bytes_written, &write_status)) break; switch (GetLastError ()) { case ERROR_OPERATION_ABORTED: continue; case ERROR_IO_PENDING: break; default: goto err; } if (!GetOverlappedResult (get_handle (), &write_status, &bytes_written, TRUE)) goto err; break; } ForceCloseHandle (write_status.hEvent); return bytes_written;err: __seterrno (); ForceCloseHandle (write_status.hEvent); return -1;}voidfhandler_serial::dump (void){ paranoid_printf ("here");}voidfhandler_serial::init (HANDLE f, DWORD flags, mode_t bin){ (void) open (NULL, flags, bin & (O_BINARY | O_TEXT));}intfhandler_serial::open (path_conv *, int flags, mode_t mode){ int res; COMMTIMEOUTS to; extern BOOL reset_com; syscall_printf ("fhandler_serial::open (%s, %p, %p)", get_name (), flags, mode); if (!(res = this->fhandler_base::open (NULL, flags, mode))) return 0; res = 1; (void) SetCommMask (get_handle (), EV_RXCHAR); set_r_no_interrupt (1); // Handled explicitly in read code overlapped_setup (); memset (&to, 0, sizeof (to)); (void) SetCommTimeouts (get_handle (), &to); /* Reset serial port to known state of 9600-8-1-no flow control on open for better behavior under Win 95. FIXME: This should only be done when explicitly opening the com port. It should not be reset if an fd is inherited. Using __progname in this way, to determine how far along in the initialization we are, is really a terrible kludge and should be fixed ASAP. */ extern char *__progname; if (reset_com && __progname) { DCB state; GetCommState (get_handle (), &state); syscall_printf ("setting initial state on %s (reset_com %d)", get_name (), reset_com); state.BaudRate = CBR_9600; state.ByteSize = 8; state.StopBits = ONESTOPBIT; state.Parity = NOPARITY; /* FIXME: correct default? */ state.fBinary = TRUE; /* binary xfer */ state.EofChar = 0; /* no end-of-data in binary mode */ state.fNull = FALSE; /* don't discard nulls in binary mode */ state.fParity = FALSE; /* ignore parity errors */ state.fErrorChar = FALSE; state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ state.fOutX = FALSE; /* disable transmission flow control */ state.fInX = FALSE; /* disable reception flow control */ state.XonChar = 0x11; state.XoffChar = 0x13; state.fOutxDsrFlow = FALSE; /* disable DSR flow control */ state.fRtsControl = RTS_CONTROL_ENABLE; /* ignore lead control except DTR */ state.fOutxCtsFlow = FALSE; /* disable output flow control */ state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR */ state.fDsrSensitivity = FALSE; /* don't assert DSR */ state.fAbortOnError = TRUE; if (!SetCommState (get_handle (), &state)) system_printf ("couldn't set initial state for %s, %E", get_name ()); } /* setting rts and dtr to known state so that ioctl() function with request TIOCMGET could return correct value of RTS and DTR lines. Important only for Win 9x systems */ if (!wincap.supports_reading_modem_output_lines ()) { if (EscapeCommFunction (get_handle (), SETDTR) == 0) system_printf ("couldn't set initial state of DTR for %s, %E", get_name ()); if (EscapeCommFunction (get_handle (), SETRTS) == 0) system_printf ("couldn't set initial state of RTS for %s, %E", get_name ()); /* even though one of above functions fail I have to set rts and dtr variables to initial value. */ rts = TIOCM_RTS; dtr = TIOCM_DTR; } SetCommMask (get_handle (), EV_RXCHAR); set_open_status (); syscall_printf ("%p = fhandler_serial::open (%s, %p, %p)", res, get_name (), flags, mode); return res;}intfhandler_serial::close (){ (void) ForceCloseHandle (io_status.hEvent); return fhandler_base::close ();}/* tcsendbreak: POSIX 7.2.2.1 *//* Break for 250-500 milliseconds if duration == 0 *//* Otherwise, units for duration are undefined */intfhandler_serial::tcsendbreak (int duration){ unsigned int sleeptime = 300000; if (duration > 0) sleeptime *= duration; if (SetCommBreak (get_handle ()) == 0) return -1; /* FIXME: need to send zero bits during duration */ usleep (sleeptime); if (ClearCommBreak (get_handle ()) == 0) return -1; syscall_printf ("0 = fhandler_serial:tcsendbreak (%d)", duration); return 0;}/* tcdrain: POSIX 7.2.2.1 */intfhandler_serial::tcdrain (void){ if (FlushFileBuffers (get_handle ()) == 0) return -1; return 0;}/* tcflow: POSIX 7.2.2.1 */intfhandler_serial::tcflow (int action){ DWORD win32action = 0; DCB dcb; char xchar; termios_printf ("action %d", action); switch (action) { case TCOOFF: win32action = SETXOFF; break; case TCOON: win32action = SETXON; break; case TCION: case TCIOFF: if (GetCommState (get_handle (), &dcb) == 0) return -1; if (action == TCION) xchar = (dcb.XonChar ? dcb.XonChar : 0x11); else xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13); if (TransmitCommChar (get_handle (), xchar) == 0) return -1; return 0; break; default: return -1; break; } if (EscapeCommFunction (get_handle (), win32action) == 0) return -1; return 0;}/* ioctl: */intfhandler_serial::ioctl (unsigned int cmd, void *buffer){ int res = 0;# define ibuffer ((int) buffer)# define ipbuffer (*(int *) buffer) DWORD ev; COMSTAT st; if (!ClearCommError (get_handle (), &ev, &st)) { __seterrno (); res = -1; } else switch (cmd) { case TCFLSH: res = tcflush (ibuffer); break; case TIOCMGET: DWORD modem_lines; if (!GetCommModemStatus (get_handle (), &modem_lines)) { __seterrno (); res = -1; } else { ipbuffer = 0; if (modem_lines & MS_CTS_ON) ipbuffer |= TIOCM_CTS; if (modem_lines & MS_DSR_ON) ipbuffer |= TIOCM_DSR; if (modem_lines & MS_RING_ON) ipbuffer |= TIOCM_RI; if (modem_lines & MS_RLSD_ON) ipbuffer |= TIOCM_CD; DWORD cb; DWORD mcr; if (!DeviceIoControl (get_handle (), IOCTL_SERIAL_GET_DTRRTS, NULL, 0, &mcr, 4, &cb, 0) || cb != 4) ipbuffer |= rts | dtr; else { if (mcr & 2) ipbuffer |= TIOCM_RTS; if (mcr & 1) ipbuffer |= TIOCM_DTR; } } break; case TIOCMSET: if (ipbuffer & TIOCM_RTS) { if (EscapeCommFunction (get_handle (), SETRTS)) rts = TIOCM_RTS; else { __seterrno (); res = -1; } } else { if (EscapeCommFunction (get_handle (), CLRRTS)) rts = 0; else { __seterrno (); res = -1; } } if (ipbuffer & TIOCM_DTR) { if (EscapeCommFunction (get_handle (), SETDTR)) dtr = TIOCM_DTR; else { __seterrno (); res = -1; } } else if (EscapeCommFunction (get_handle (), CLRDTR)) dtr = 0; else { __seterrno (); res = -1; } break; case TIOCINQ: if (ev & CE_FRAME || ev & CE_IOE || ev & CE_OVERRUN || ev & CE_RXOVER || ev & CE_RXPARITY) { set_errno (EINVAL); /* FIXME: Use correct errno */ res = -1; } else ipbuffer = st.cbInQue; break; default: set_errno (ENOSYS); res = -1; break; } termios_printf ("%d = ioctl (%p, %p)", res, cmd, buffer);# undef ibuffer# undef ipbuffer return res;}/* tcflush: POSIX 7.2.2.1 */intfhandler_serial::tcflush (int queue){ if (queue == TCOFLUSH || queue == TCIOFLUSH) PurgeComm (get_handle (), PURGE_TXABORT | PURGE_TXCLEAR); if (queue == TCIFLUSH || queue == TCIOFLUSH) /* Input flushing by polling until nothing turns up (we stop after 1000 chars anyway) */ for (int max = 1000; max > 0; max--) { COMSTAT st; if (!PurgeComm (get_handle (), PURGE_RXABORT | PURGE_RXCLEAR)) break; low_priority_sleep (100);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -