public void shutDown() { for (Port port : m_OpenPorts.values()) { try { log = log && log(1, "shutDown() closing port %d\n", port.m_FD); port.close(); } catch (Exception e) { // should never happen e.printStackTrace(); } } }
public int tcdrain(int fd) { Port port = getPort(fd); if (port == null) return -1; try { synchronized (port.m_WrBuffer) { if (!FlushFileBuffers(port.m_Comm)) port.fail(); return 0; } } catch (Fail f) { return -1; } }
public int fcntl(int fd, int cmd, int arg) { Port port = getPort(fd); if (port == null) return -1; if (F_SETFL == cmd) port.m_OpenFlags = arg; else if (F_GETFL == cmd) return port.m_OpenFlags; else { m_ErrNo = ENOTSUP; return -1; } return 0; }
public int tcsendbreak(int fd, int duration) { Port port = getPort(fd); if (port == null) return -1; try { if (!SetCommBreak(port.m_Comm)) port.fail(); nanoSleep(duration * 250000000); if (!ClearCommBreak(port.m_Comm)) port.fail(); return 0; } catch (Fail f) { return -1; } }
public int tcflush(int fd, int queue) { Port port = getPort(fd); if (port == null) return -1; try { if (queue == TCIFLUSH) { if (!PurgeComm(port.m_Comm, PURGE_RXABORT)) port.fail(); } else if (queue == TCOFLUSH) { if (!PurgeComm(port.m_Comm, PURGE_TXABORT)) port.fail(); } else if (queue == TCIOFLUSH) { if (!PurgeComm(port.m_Comm, PURGE_TXABORT)) port.fail(); if (!PurgeComm(port.m_Comm, PURGE_RXABORT)) port.fail(); } else { m_ErrNo = ENOTSUP; return -1; } return 0; } catch (Fail f) { return -1; } }
public int ioctl(int fd, int cmd, int[] arg) { Port port = getPort(fd); if (port == null) return -1; try { if (cmd == FIONREAD) { clearCommErrors(port); arg[0] = port.m_COMSTAT.cbInQue; return 0; } else if (cmd == TIOCMSET) { int a = arg[0]; if ((a & TIOCM_DTR) != 0) port.MSR |= TIOCM_DTR; else port.MSR &= ~TIOCM_DTR; if (!EscapeCommFunction(port.m_Comm, ((a & TIOCM_DTR) != 0) ? SETDTR : CLRDTR)) port.fail(); if ((a & TIOCM_RTS) != 0) port.MSR |= TIOCM_RTS; else port.MSR &= ~TIOCM_RTS; if (!EscapeCommFunction(port.m_Comm, ((a & TIOCM_RTS) != 0) ? SETRTS : CLRRTS)) port.fail(); return 0; } else if (cmd == TIOCMGET) { int[] stat = {0}; if (!GetCommModemStatus(port.m_Comm, stat)) port.fail(); int s = stat[0]; int a = arg[0]; if ((s & MS_RLSD_ON) != 0) a |= TIOCM_CAR; else a &= ~TIOCM_CAR; if ((s & MS_RING_ON) != 0) a |= TIOCM_RNG; else a &= ~TIOCM_RNG; if ((s & MS_DSR_ON) != 0) a |= TIOCM_DSR; else a &= ~TIOCM_DSR; if ((s & MS_CTS_ON) != 0) a |= TIOCM_CTS; else a &= ~TIOCM_CTS; if ((port.MSR & TIOCM_DTR) != 0) a |= TIOCM_DTR; else a &= ~TIOCM_DTR; if ((port.MSR & TIOCM_RTS) != 0) a |= TIOCM_RTS; else a &= ~TIOCM_RTS; arg[0] = a; return 0; } else { m_ErrNo = ENOTSUP; return -1; } } catch (Fail f) { return -1; } }
public int select(int n, FDSet readfds, FDSet writefds, FDSet exceptfds, TimeVal timeout) { // long T0 = System.currentTimeMillis(); int ready = 0; LinkedList<Port> locked = new LinkedList<Port>(); try { try { LinkedList<Port> waiting = new LinkedList<Port>(); for (int fd = 0; fd < n; fd++) { boolean rd = FD_ISSET(fd, readfds); boolean wr = FD_ISSET(fd, writefds); FD_CLR(fd, readfds); FD_CLR(fd, writefds); if (rd || wr) { Port port = getPort(fd); if (port == null) return -1; try { port.lock(); locked.add(port); clearCommErrors(port); // check if there is data to be read, as WaitCommEvent // does check for only *new* data that and thus // might wait indefinitely if select() is called twice // without first reading away all data if (rd && port.m_COMSTAT.cbInQue > 0) { FD_SET(fd, readfds); ready++; } if (wr && port.m_COMSTAT.cbOutQue == 0) { FD_SET(fd, writefds); ready++; } if (!ResetEvent(port.m_SelOVL.hEvent)) port.fail(); int flags = 0; if (rd) flags |= EV_RXCHAR; if (wr) flags |= EV_TXEMPTY; if (!SetCommMask(port.m_Comm, flags)) port.fail(); if (WaitCommEvent(port.m_Comm, port.m_EventFlags, port.m_SelOVL)) { if (!GetOverlappedResult(port.m_Comm, port.m_SelOVL, port.m_SelN, false)) port.fail(); // actually it seems that overlapped // WaitCommEvent never returns true so we never get here ready = maskToFDSets(port, readfds, writefds, exceptfds, ready); } else { // FIXME if the port dies on us what happens if (GetLastError() != ERROR_IO_PENDING) port.fail(); waiting.add(port); } } catch (InterruptedException ie) { m_ErrNo = EINTR; return -1; } } } if (ready == 0) { int waitn = waiting.size(); if (waitn > 0) { HANDLE[] wobj = new HANDLE[waiting.size() * 2]; int i = 0; for (Port port : waiting) { wobj[i++] = port.m_SelOVL.hEvent; wobj[i++] = port.m_CancelWaitSema4; } int tout = timeout != null ? (int) (timeout.tv_sec * 1000 + timeout.tv_usec / 1000) : INFINITE; // int res = WaitForSingleObject(wobj[0], tout); int res = WaitForMultipleObjects(waitn * 2, wobj, false, tout); if (res == WAIT_TIMEOUT) { // work around the fact that sometimes we miss // events for (Port port : waiting) { clearCommErrors(port); int[] mask = {0}; if (!GetCommMask(port.m_Comm, mask)) port.fail(); if (port.m_COMSTAT.cbInQue > 0 && ((mask[0] & EV_RXCHAR) != 0)) { FD_SET(port.m_FD, readfds); log = log && log(1, "missed EV_RXCHAR event\n"); return 1; } if (port.m_COMSTAT.cbOutQue == 0 && ((mask[0] & EV_TXEMPTY) != 0)) { FD_SET(port.m_FD, writefds); log = log && log(1, "missed EV_TXEMPTY event\n"); return 1; } } } if (res != WAIT_TIMEOUT) { i = (res - WAIT_OBJECT_0) / 2; if (i < 0 || i >= waitn) throw new Fail(); Port port = waiting.get(i); if (!GetOverlappedResult(port.m_Comm, port.m_SelOVL, port.m_SelN, false)) port.fail(); ready = maskToFDSets(port, readfds, writefds, exceptfds, ready); } } else { if (timeout != null) nanoSleep(timeout.tv_sec * 1000000000L + timeout.tv_usec * 1000); else { m_ErrNo = EINVAL; return -1; } return 0; } } } catch (Fail f) { return -1; } } finally { for (Port port : locked) port.unlock(); } // long T1 = System.currentTimeMillis(); // System.err.println("select() " + (T1 - T0)); return ready; }
private void clearCommErrors(Port port) throws Fail { synchronized (port.m_COMSTAT) { if (!ClearCommError(port.m_Comm, port.m_ClearErr, port.m_COMSTAT)) port.fail(); } }
// FIXME this needs serious code review from people who know this stuff... public int updateFromTermios(Port port) throws Fail { Termios tios = port.m_Termios; int c_speed = tios.c_ospeed; int c_cflag = tios.c_cflag; int c_iflag = tios.c_iflag; int c_oflag = tios.c_oflag; if (c_speed != port.m_c_speed || c_cflag != port.m_c_cflag || c_iflag != port.m_c_iflag || c_oflag != port.m_c_oflag) { DCB dcb = port.m_DCB; if (!GetCommState(port.m_Comm, dcb)) port.fail(); dcb.DCBlength = dcb.size(); dcb.BaudRate = c_speed; if (tios.c_ospeed != tios.c_ispeed) log(0, "c_ospeed (%d) != c_ispeed (%d)\n", tios.c_ospeed, tios.c_ispeed); int flags = 0; // rxtx does: if ( s_termios->c_iflag & ISTRIP ) dcb.fBinary = FALSE; // but Winapi doc says fBinary always true flags |= fBinary; if ((c_cflag & PARENB) != 0) flags |= fParity; if ((c_iflag & IXON) != 0) flags |= fOutX; if ((c_iflag & IXOFF) != 0) flags |= fInX; if ((c_iflag & IXANY) != 0) flags |= fTXContinueOnXoff; if ((c_iflag & CRTSCTS) != 0) { flags |= fRtsControl; flags |= fOutxCtsFlow; } // Following have no corresponding functionality in unix termios // fOutxDsrFlow = 0x00000008; // fDtrControl = 0x00000030; // fDsrSensitivity = 0x00000040; // fErrorChar = 0x00000400; // fNull = 0x00000800; // fAbortOnError = 0x00004000; // fDummy2 = 0xFFFF8000; dcb.fFlags = flags; // Don't touch these, windows seems to use: XonLim 2048 XoffLim 512 and who am I to argue with // those // dcb.XonLim = 0; // dcb.XoffLim = 128; byte cs = 8; int csize = c_cflag & CSIZE; if (csize == CS5) cs = 5; if (csize == CS6) cs = 6; if (csize == CS7) cs = 7; if (csize == CS8) cs = 8; dcb.ByteSize = cs; if ((c_cflag & PARENB) != 0) { if ((c_cflag & PARODD) != 0 && (c_cflag & CMSPAR) != 0) dcb.Parity = MARKPARITY; else if ((c_cflag & PARODD) != 0) dcb.Parity = ODDPARITY; else if ((c_cflag & CMSPAR) != 0) dcb.Parity = SPACEPARITY; else dcb.Parity = EVENPARITY; } else dcb.Parity = NOPARITY; dcb.StopBits = (c_cflag & CSTOPB) != 0 ? TWOSTOPBITS : ONESTOPBIT; dcb.XonChar = tios.c_cc[ VSTART]; // In theory these could change but they only get updated if the // baudrate/char size changes so this could be a time bomb dcb.XoffChar = tios.c_cc[ VSTOP]; // In practice in PJC these are never changed so updating on the first pass is // enough dcb.ErrorChar = 0; // rxtx has some thing like // if ( EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING | ( EV_RLSD & EV_RXFLAG ) // ) // dcb.EvtChar = '\n'; // else // dcb.EvtChar = '\0'; // But those are all defines so there is something fishy there? dcb.EvtChar = '\n'; dcb.EofChar = tios.c_cc[VEOF]; if (!SetCommState(port.m_Comm, dcb)) port.fail(); port.m_c_speed = c_speed; port.m_c_cflag = c_cflag; port.m_c_iflag = c_iflag; port.m_c_oflag = c_oflag; } int vmin = port.m_Termios.c_cc[VMIN] & 0xFF; int vtime = (port.m_Termios.c_cc[VTIME] & 0xFF) * 100; if (vmin != port.m_VMIN || vtime != port.m_VTIME) { COMMTIMEOUTS touts = port.m_Timeouts; // There are really no write timeouts in classic unix termios // FIXME test that we can still interrupt the tread touts.WriteTotalTimeoutConstant = 0; touts.WriteTotalTimeoutMultiplier = 0; if (vmin == 0 && vtime == 0) { // VMIN = 0 and VTIME = 0 => totally non blocking,if data is // available, return it, ie this is poll operation touts.ReadIntervalTimeout = MAXDWORD; touts.ReadTotalTimeoutConstant = 0; touts.ReadTotalTimeoutMultiplier = 0; } if (vmin == 0 && vtime > 0) { // VMIN = 0 and VTIME > 0 => timed read, return as soon as data is // available, VTIME = total time touts.ReadIntervalTimeout = 0; touts.ReadTotalTimeoutConstant = vtime; touts.ReadTotalTimeoutMultiplier = 0; } if (vmin > 0 && vtime > 0) { // VMIN > 0 and VTIME > 0 => blocks until VMIN chars has arrived or between chars expired, // note that this will block if nothing arrives touts.ReadIntervalTimeout = vtime; touts.ReadTotalTimeoutConstant = 0; touts.ReadTotalTimeoutMultiplier = 0; } if (vmin > 0 && vtime == 0) { // VMIN > 0 and VTIME = 0 => blocks until VMIN characters have been // received touts.ReadIntervalTimeout = 0; touts.ReadTotalTimeoutConstant = 0; touts.ReadTotalTimeoutMultiplier = 0; } if (!SetCommTimeouts(port.m_Comm, port.m_Timeouts)) port.fail(); port.m_VMIN = vmin; port.m_VTIME = vtime; log = log && log( 2, "vmin %d vtime %d ReadIntervalTimeout %d ReadTotalTimeoutConstant %d ReadTotalTimeoutMultiplier %d\n", vmin, vtime, touts.ReadIntervalTimeout, touts.ReadTotalTimeoutConstant, touts.ReadTotalTimeoutMultiplier); } return 0; }
public int close(int fd) { Port port = getPort(fd); if (port == null) return -1; port.close(); return 0; }
public int write(int fd, byte[] buffer, int length) { Port port = getPort(fd); if (port == null) return -1; synchronized (port.m_WrBuffer) { try { if (port.m_WritePending > 0) { while (true) { int res = WaitForSingleObject(port.m_WrOVL.hEvent, INFINITE); if (res == WAIT_TIMEOUT) { clearCommErrors(port); log = log && log( 1, "write pending, cbInQue %d cbOutQue %d\n", port.m_COMSTAT.cbInQue, port.m_COMSTAT.cbOutQue); continue; } if (!GetOverlappedResult(port.m_Comm, port.m_WrOVL, port.m_WrN, false)) port.fail(); if (port.m_WrN[0] != port.m_WritePending) // I exptect this is never going to happen, if it does new RuntimeException( "Windows OVERLAPPED WriteFile failed to write all, tried to write " + port.m_WritePending + " but got " + port.m_WrN[0]); break; } port.m_WritePending = 0; } if ((port.m_OpenFlags & O_NONBLOCK) != 0) { if (!ClearCommError(port.m_Comm, port.m_WrErr, port.m_WrStat)) port.fail(); int room = (int) port.m_WrBuffer.size() - port.m_WrStat.cbOutQue; if (length > room) length = room; } if (!ResetEvent(port.m_WrOVL.hEvent)) port.fail(); if (length > port.m_WrBuffer.size()) length = (int) port.m_WrBuffer.size(); port.m_WrBuffer.write(0, buffer, 0, length); // copy from buffer to Memory boolean ok = WriteFile(port.m_Comm, port.m_WrBuffer, length, port.m_WrN, port.m_WrOVL); if (!ok) { if (GetLastError() != ERROR_IO_PENDING) port.fail(); port.m_WritePending = length; } // return length; // port.m_WrN[0]; } catch (Fail f) { return -1; } } }
public int read(int fd, byte[] buffer, int length) { Port port = getPort(fd); if (port == null) return -1; synchronized (port.m_RdBuffer) { try { // limit reads to internal buffer size if (length > port.m_RdBuffer.size()) length = (int) port.m_RdBuffer.size(); if (length == 0) return 0; if ((port.m_OpenFlags & O_NONBLOCK) != 0) { clearCommErrors(port); int available = port.m_COMSTAT.cbInQue; if (available == 0) { m_ErrNo = EAGAIN; return -1; } length = min(length, available); } else { clearCommErrors(port); int available = port.m_COMSTAT.cbInQue; int vtime = 0xff & port.m_Termios.c_cc[VTIME]; int vmin = 0xff & port.m_Termios.c_cc[VMIN]; if (vmin == 0 && vtime == 0) { // VMIN = 0 and VTIME = 0 => totally non blocking,if data is // available, return it, ie this is poll operation // For reference below commented out is how timeouts are set for this vtime/vmin combo // touts.ReadIntervalTimeout = MAXDWORD; // touts.ReadTotalTimeoutConstant = 0; // touts.ReadTotalTimeoutMultiplier = 0; if (available == 0) return 0; length = min(length, available); } if (vmin == 0 && vtime > 0) { // VMIN = 0 and VTIME > 0 => timed read, return as soon as data is // available, VTIME = total time // For reference below commented out is how timeouts are set for this vtime/vmin combo // touts.ReadIntervalTimeout = 0; // touts.ReadTotalTimeoutConstant = vtime; // touts.ReadTotalTimeoutMultiplier = 0; // NOTE to behave like unix we should probably wait until there is something available // and then try to do a read as many bytes as are available bytes at that point in time. // As this is coded now, this will attempt to read as many bytes as requested and this // may end up // spending vtime in the read when a unix would return less bytes but as soon as they // become // available. } if (vmin > 0 && vtime > 0) { // VMIN > 0 and VTIME > 0 => blocks until VMIN chars has arrived or between chars // expired, // note that this will block if nothing arrives // For reference below commented out is how timeouts are set for this vtime/vmin combo // touts.ReadIntervalTimeout = vtime; // touts.ReadTotalTimeoutConstant = 0; // touts.ReadTotalTimeoutMultiplier = 0; length = min(max(vmin, available), length); } if (vmin > 0 && vtime == 0) { // VMIN > 0 and VTIME = 0 => blocks until VMIN characters have been // received // For reference below commented out is how timeouts are set for this vtime/vmin combo // touts.ReadIntervalTimeout = 0; // touts.ReadTotalTimeoutConstant = 0; // touts.ReadTotalTimeoutMultiplier = 0; length = min(max(vmin, available), length); } } if (!ResetEvent(port.m_RdOVL.hEvent)) port.fail(); if (!ReadFile(port.m_Comm, port.m_RdBuffer, length, port.m_RdN, port.m_RdOVL)) { if (GetLastError() != ERROR_IO_PENDING) port.fail(); if (WaitForSingleObject(port.m_RdOVL.hEvent, INFINITE) != WAIT_OBJECT_0) port.fail(); if (!GetOverlappedResult(port.m_Comm, port.m_RdOVL, port.m_RdN, true)) port.fail(); } port.m_RdBuffer.read(0, buffer, 0, port.m_RdN[0]); return port.m_RdN[0]; } catch (Fail ie) { return -1; } } }
public int open(String filename, int flags) { Port port = new Port(); port.m_OpenFlags = flags; try { if (!filename.startsWith("\\\\")) filename = "\\\\.\\" + filename; port.m_Comm = CreateFileW( new WString(filename), GENERIC_READ | GENERIC_WRITE, 0, null, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, null); if (INVALID_HANDLE_VALUE == port.m_Comm) { if (GetLastError() == ERROR_FILE_NOT_FOUND) m_ErrNo = ENOENT; else m_ErrNo = EBUSY; port.fail(); } if (!SetupComm(port.m_Comm, (int) port.m_RdBuffer.size(), (int) port.m_WrBuffer.size())) port.fail(); // FIXME what would be appropriate error code here cfmakeraw(port.m_Termios); cfsetispeed(port.m_Termios, B9600); cfsetospeed(port.m_Termios, B9600); port.m_Termios.c_cc[VTIME] = 0; port.m_Termios.c_cc[VMIN] = 0; updateFromTermios(port); port.m_RdOVL.writeField("hEvent", CreateEventA(null, true, false, null)); if (port.m_RdOVL.hEvent == INVALID_HANDLE_VALUE) port.fail(); port.m_WrOVL.writeField("hEvent", CreateEventA(null, true, false, null)); if (port.m_WrOVL.hEvent == INVALID_HANDLE_VALUE) port.fail(); port.m_SelOVL.writeField("hEvent", CreateEventA(null, true, false, null)); if (port.m_SelOVL.hEvent == INVALID_HANDLE_VALUE) port.fail(); return port.m_FD; } catch (Exception f) { if (port != null) port.close(); return -1; } }