/* * Copyright (c) 2011, Kustaa Nyholm / SpareTimeLabs * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * Neither the name of the Kustaa Nyholm or SpareTimeLabs nor the names of its * contributors may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ package jtermios.solaris; import com.sun.jna.*; import com.sun.jna.ptr.IntByReference; import java.io.File; import java.util.*; import java.util.regex.Pattern; import jtermios.JTermios; import jtermios.Pollfd; import jtermios.Termios; import jtermios.TimeVal; import static jtermios.JTermios.*; import static jtermios.JTermios.JTermiosLogging.log; public class JTermiosImpl implements jtermios.JTermios.JTermiosInterface { private static String DEVICE_DIR_PATH = "/dev/"; static C_lib_DirectMapping m_ClibDM; static C_lib m_Clib; static NonDirectCLib m_ClibND; static { Native.setPreserveLastError(true); m_ClibND = (NonDirectCLib) Native.loadLibrary(Platform.C_LIBRARY_NAME, NonDirectCLib.class); Native.register(C_lib_DirectMapping.class, NativeLibrary.getInstance(Platform.C_LIBRARY_NAME)); m_ClibDM = new C_lib_DirectMapping(); m_Clib = m_ClibDM; } public static class C_lib_DirectMapping implements C_lib { native public int pipe(int[] fds); native public int tcdrain(int fd); native public void cfmakeraw(termios termios); native public int fcntl(int fd, int cmd, int arg); native public int ioctl(int fd, int cmd, int[] arg); native public int open(String path, int flags); native public int close(int fd); native public int tcgetattr(int fd, termios termios); native public int tcsetattr(int fd, int cmd, termios termios); native public int cfsetispeed(termios termios, NativeLong i); native public int cfsetospeed(termios termios, NativeLong i); native public NativeLong cfgetispeed(termios termios); native public NativeLong cfgetospeed(termios termios); native public NativeSize write(int fd, byte[] buffer, NativeSize count); native public NativeSize read(int fd, byte[] buffer, NativeSize count); native public int tcflush(int fd, int qs); native public void perror(String msg); native public int tcsendbreak(int fd, int duration); } public interface C_lib extends com.sun.jna.Library { public int pipe(int[] fds); public int tcdrain(int fd); public void cfmakeraw(termios termios); public int fcntl(int fd, int cmd, int arg); public int ioctl(int fd, int cmd, int[] arg); public int open(String path, int flags); public int close(int fd); public int tcgetattr(int fd, termios termios); public int tcsetattr(int fd, int cmd, termios termios); public int cfsetispeed(termios termios, NativeLong i); public int cfsetospeed(termios termios, NativeLong i); public NativeLong cfgetispeed(termios termios); public NativeLong cfgetospeed(termios termios); public NativeSize write(int fd, byte[] buffer, NativeSize count); public NativeSize read(int fd, byte[] buffer, NativeSize count); public int tcflush(int fd, int qs); public void perror(String msg); public int tcsendbreak(int fd, int duration); } public interface NonDirectCLib extends com.sun.jna.Library { public int select(int n, fd_set read, fd_set write, fd_set error, timeval timeout); public int poll(pollfd.ByReference pfds, int nfds, int timeout); } static public class timeval extends Structure { public NativeLong tv_sec; public NativeLong tv_usec; @Override protected List getFieldOrder() { return Arrays.asList(// "tv_sec",// "tv_usec"// ); } public timeval(jtermios.TimeVal timeout) { tv_sec = new NativeLong(timeout.tv_sec); tv_usec = new NativeLong(timeout.tv_usec); } } static public class pollfd extends Structure { public static class ByReference extends pollfd implements Structure.ByReference { } public int fd; public short events; public short revents; @Override protected List getFieldOrder() { return Arrays.asList(// "fd",// "events",// "revents"// ); } public pollfd() { } public pollfd(Pollfd pfd) { fd = pfd.fd; events = pfd.events; revents = pfd.revents; } } static public class fd_set extends Structure implements FDSet { private final static int NFBBITS = NativeLong.SIZE * 8; private final static int fd_count = 1024; public NativeLong[] fd_array = new NativeLong[(fd_count + NFBBITS - 1) / NFBBITS]; public fd_set() { for (int i = 0; i < fd_array.length; ++i) { fd_array[i] = new NativeLong(); } } @Override protected List getFieldOrder() { return Arrays.asList(// "fd_array"// ); } public void FD_SET(int fd) { fd_array[fd / NFBBITS].setValue(fd_array[fd / NFBBITS].longValue() | (1L << (fd % NFBBITS))); } public boolean FD_ISSET(int fd) { return (fd_array[fd / NFBBITS].longValue() & (1L << (fd % NFBBITS))) != 0; } public void FD_ZERO() { for (NativeLong fd : fd_array) { fd.setValue(0L); } } public void FD_CLR(int fd) { fd_array[fd / NFBBITS].setValue(fd_array[fd / NFBBITS].longValue() & ~(1L << (fd % NFBBITS))); } } static public class termios extends Structure { public int c_iflag; public int c_oflag; public int c_cflag; public int c_lflag; public byte[] c_cc = new byte[32]; @Override protected List getFieldOrder() { return Arrays.asList(// "c_iflag",// "c_oflag",// "c_cflag",// "c_lflag",// "c_cc"// ); } public termios() { } public termios(jtermios.Termios t) { c_iflag = t.c_iflag; c_oflag = t.c_oflag; c_cflag = t.c_cflag; c_lflag = t.c_lflag; System.arraycopy(t.c_cc, 0, c_cc, 0, Math.min(t.c_cc.length, c_cc.length)); } public void update(jtermios.Termios t) { t.c_iflag = c_iflag; t.c_oflag = c_oflag; t.c_cflag = c_cflag; t.c_lflag = c_lflag; System.arraycopy(c_cc, 0, t.c_cc, 0, Math.min(t.c_cc.length, c_cc.length)); } } public JTermiosImpl() { log = log && log(1, "instantiating %s\n", getClass().getCanonicalName()); // sys/filio.h stuff FIONREAD = 0x4004667F; //fcntl.h stuff O_RDWR = 0x00000002; O_NONBLOCK = 0x00000080; O_NOCTTY = 0x00000800; O_NDELAY = 0x00000004; F_GETFL = 0x00000003; F_SETFL = 0x00000004; //errno.h stuff EAGAIN = 11; EBADF = 9; EACCES = 22; EEXIST = 17; EINTR = 4; EINVAL = 22; EIO = 5; EISDIR = 21; ELOOP = 90; EMFILE = 24; ENAMETOOLONG = 78; ENFILE = 23; ENOENT = 2; ENOSR = 63; ENOSPC = 28; ENOTDIR = 20; ENXIO = 6; EOVERFLOW = 79; EROFS = 30; ENOTSUP = 48; //termios.h stuff TIOCM_RNG = 0x00000080; TIOCM_CAR = 0x00000040; IGNBRK = 0x00000001; BRKINT = 0x00000002; IGNPAR = 0x00000004; PARMRK = 0x00000008; INLCR = 0x00000040; IGNCR = 0x00000080; ICRNL = 0x00000100; ECHONL = 0x00000040; IEXTEN = 0x00008000; CLOCAL = 0x00000800; OPOST = 0x00000001; VSTART = 0x00000008; TCSANOW = 0x0000540E; VSTOP = 0x00000009; VMIN = 0x00000004; VTIME = 0x00000005; VEOF = 0x00000004; TIOCMGET = 0x0000741D; TIOCM_CTS = 0x00000020; TIOCM_DSR = 0x00000100; TIOCM_RI = 0x00000080; TIOCM_CD = 0x00000040; TIOCM_DTR = 0x00000002; TIOCM_RTS = 0x00000004; ICANON = 0x00000002; ECHO = 0x00000008; ECHOE = 0x00000010; ISIG = 0x00000001; TIOCMSET = 0x0000741A; IXON = 0x00000400; IXOFF = 0x00001000; IXANY = 0x00000800; CRTSCTS = 0x80000000; TCSADRAIN = 0x0000540F; INPCK = 0x00000010; ISTRIP = 0x00000020; CSIZE = 0x00000030; TCIFLUSH = 0x00000000; TCOFLUSH = 0x00000001; TCIOFLUSH = 0x00000002; CS5 = 0x00000000; CS6 = 0x00000010; CS7 = 0x00000020; CS8 = 0x00000030; CSTOPB = 0x00000040; CREAD = 0x00000080; PARENB = 0x00000100; PARODD = 0x00000200; B0 = 0; B50 = 1; B75 = 2; B110 = 3; B134 = 4; B150 = 5; B200 = 6; B300 = 7; B600 = 8; B1200 = 8; B1800 = 10; B2400 = 11; B4800 = 12; B9600 = 13; B19200 = 14; B38400 = 15; B57600 = 16; B76800 = 17; B115200 = 18; B230400 = 20; //poll.h stuff POLLIN = 0x0001; POLLPRI = 0x0002; POLLOUT = 0x0004; POLLERR = 0x0008; POLLNVAL = 0x0020; //select.h stuff } public int errno() { return Native.getLastError(); } public void cfmakeraw(Termios termios) { termios t = new termios(termios); m_Clib.cfmakeraw(t); t.update(termios); } public int fcntl(int fd, int cmd, int arg) { return m_Clib.fcntl(fd, cmd, arg); } public int tcdrain(int fd) { return m_Clib.tcdrain(fd); } public int cfgetispeed(Termios termios) { return m_Clib.cfgetispeed(new termios(termios)).intValue(); } public int cfgetospeed(Termios termios) { return m_Clib.cfgetospeed(new termios(termios)).intValue(); } public int cfsetispeed(Termios termios, int speed) { termios t = new termios(termios); int ret = m_Clib.cfsetispeed(t, new NativeLong(speed)); t.update(termios); return ret; } public int cfsetospeed(Termios termios, int speed) { termios t = new termios(termios); int ret = m_Clib.cfsetospeed(t, new NativeLong(speed)); t.update(termios); return ret; } public int open(String s, int t) { if (s != null && !s.startsWith("/")) { s = DEVICE_DIR_PATH + s; } return m_Clib.open(s, t); } public int read(int fd, byte[] buffer, int len) { return m_Clib.read(fd, buffer, new NativeSize(len)).intValue(); } public int write(int fd, byte[] buffer, int len) { return m_Clib.write(fd, buffer, new NativeSize(len)).intValue(); } public int close(int fd) { return m_Clib.close(fd); } public int tcflush(int fd, int b) { return m_Clib.tcflush(fd, b); } public int tcgetattr(int fd, Termios termios) { termios t = new termios(); int ret = m_Clib.tcgetattr(fd, t); t.update(termios); return ret; } public void perror(String msg) { m_Clib.perror(msg); } public int tcsendbreak(int fd, int duration) { // If duration is not zero, it sends zero-valued bits for duration*N seconds, // where N is at least 0.25, and not more than 0.5. return m_Clib.tcsendbreak(fd, duration / 250); } public int tcsetattr(int fd, int cmd, Termios termios) { return m_Clib.tcsetattr(fd, cmd, new termios(termios)); } public int select(int nfds, FDSet rfds, FDSet wfds, FDSet efds, TimeVal timeout) { timeval tout = null; if (timeout != null) { tout = new timeval(timeout); } return m_ClibND.select(nfds, (fd_set) rfds, (fd_set) wfds, (fd_set) efds, tout); } public int poll(Pollfd fds[], int nfds, int timeout) { if (nfds <= 0 || nfds > fds.length) { throw new java.lang.IllegalArgumentException("nfds " + nfds + " must be <= fds.length " + fds.length); } pollfd.ByReference parampfds = new pollfd.ByReference(); pollfd[] pfds = (pollfd[]) parampfds.toArray(nfds); for (int i = 0; i < nfds; i++) { pfds[i].fd = fds[i].fd; pfds[i].events = fds[i].events; } int ret = m_ClibND.poll(parampfds, nfds, timeout); for (int i = 0; i < nfds; i++) { fds[i].revents = pfds[i].revents; } return ret; } public boolean canPoll() { return true; } public FDSet newFDSet() { return new fd_set(); } public int ioctl(int fd, int cmd, int... data) { // At this time, all ioctl commands we have defined are either no parameter or 4 byte parameter. return m_Clib.ioctl(fd, cmd, data); } public String getPortNamePattern() { return ".*"; } public List<String> getPortList() { File dir = new File(DEVICE_DIR_PATH); if (!dir.isDirectory()) { log = log && log(1, "device directory %s does not exist\n", DEVICE_DIR_PATH); return null; } String[] devs = dir.list(); LinkedList<String> list = new LinkedList<String>(); Pattern p = JTermios.getPortNamePattern(this); if (devs != null) { for (int i = 0; i < devs.length; i++) { String s = devs[i]; if (p.matcher(s).matches()) list.add(s); } } return list; } public void shutDown() { } public int setspeed(int fd, Termios termios, int speed) { int br = speed; switch (speed) { case 50: br = B50; break; case 75: br = B75; break; case 110: br = B110; break; case 134: br = B134; break; case 150: br = B150; break; case 200: br = B200; break; case 300: br = B300; break; case 600: br = B600; break; case 1200: br = B1200; break; case 1800: br = B1800; break; case 2400: br = B2400; break; case 4800: br = B4800; break; case 9600: br = B9600; break; case 19200: br = B19200; break; case 38400: br = B38400; break; case 7200: br = B7200; break; case 14400: br = B14400; break; case 28800: br = B28800; break; case 57600: br = B57600; break; case 76800: br = B76800; break; case 115200: br = B115200; break; case 230400: br = B230400; break; } int r; if ((r = cfsetispeed(termios, br)) != 0) return r; if ((r = cfsetospeed(termios, br)) != 0) return r; if ((r = tcsetattr(fd, TCSANOW, termios)) != 0) return r; return 0; } public int pipe(int[] fds) { return m_Clib.pipe(fds); } }