jtermios.linux.JTermiosImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of purejavacomm Show documentation
Show all versions of purejavacomm Show documentation
PureJavaComm is an Application Programmin Interface (API) for accessing serial ports from Java. PureJavaComm aims to be a drop-in replacement for Sun's (now Oracle) abandoned JavaComm and an easier to deploy alternative to RXTX.
/*
* 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.linux;
import com.sun.jna.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
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;
}
private final static int TIOCGSERIAL = 0x0000541E;
private final static int TIOCSSERIAL = 0x0000541F;
private final static int ASYNC_SPD_MASK = 0x00001030;
private final static int ASYNC_SPD_CUST = 0x00000030;
private final static int[] m_BaudRates
= { //
50, 0000001, //
75, 0000002, //
110, 0000003, //
134, 0000004, //
150, 0000005, //
200, 0000006, //
300, 0000007, //
600, 0000010, //
1200, 0000011, //
1800, 0000012, //
2400, 0000013, //
4800, 0000014, //
9600, 0000015, //
19200, 0000016, //
38400, 0000017, //
57600, 0010001, //
115200, 0010002, //
230400, 0010003, //
460800, 0010004, //
500000, 0010005, //
576000, 0010006, //
921600, 0010007, //
1000000, 0010010, //
1152000, 0010011, //
1500000, 0010012, //
2000000, 0010013, //
2500000, 0010014, //
3000000, 0010015, //
3500000, 0010016, //
4000000, 0010017 //
};
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 ioctl(int fd, int cmd, serial_struct 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, int i);
native public int cfsetospeed(termios termios, int i);
native public int cfgetispeed(termios termios);
native public int 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 ioctl(int fd, int cmd, serial_struct 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, int i);
public int cfsetospeed(termios termios, int i);
public int cfgetispeed(termios termios);
public int 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)));
}
}
public static class serial_struct extends Structure {
public int type;
public int line;
public int port;
public int irq;
public int flags;
public int xmit_fifo_size;
public int custom_divisor;
public int baud_base;
public short close_delay;
public short io_type;
//public char io_type;
//public char reserved_char;
public int hub6;
public short closing_wait;
public short closing_wait2;
public Pointer iomem_base;
public short iomem_reg_shift;
public int port_high;
public NativeLong iomap_base;
@Override
protected List getFieldOrder() {
return Arrays.asList(//
"type",//
"line",//
"port",//
"irq",//
"flags",//
"xmit_fifo_size",//
"custom_divisor",//
"baud_base",//
"close_delay",//
"io_type",//
//public char io_type;
//public char reserved_char;
"hub6",//
"closing_wait",//
"closing_wait2",//
"iomem_base",//
"iomem_reg_shift",//
"port_high",//
"iomap_base"//
);
}
};
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_line;
public byte[] c_cc = new byte[32];
public int c_ispeed;
public int c_ospeed;
@Override
protected List getFieldOrder() {
return Arrays.asList(//
"c_iflag",//
"c_oflag",//
"c_cflag",//
"c_lflag",//
"c_line",//
"c_cc",//
"c_ispeed",//
"c_ospeed"//
);
}
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, t.c_cc.length);
c_ispeed = t.c_ispeed;
c_ospeed = t.c_ospeed;
}
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, t.c_cc.length);
t.c_ispeed = c_ispeed;
t.c_ospeed = c_ospeed;
}
}
public JTermiosImpl() {
log = log && log(1, "instantiating %s\n", getClass().getCanonicalName());
//linux/serial.h stuff
FIONREAD = 0x541B; // Looked up manually
//fcntl.h stuff
O_RDWR = 0x00000002;
O_NONBLOCK = 0x00000800;
O_NOCTTY = 0x00000100;
O_NDELAY = 0x00000800;
F_GETFL = 0x00000003;
F_SETFL = 0x00000004;
//errno.h stuff
EAGAIN = 11;
EACCES = 13;
EEXIST = 17;
EINTR = 4;
EINVAL = 22;
EIO = 5;
EISDIR = 21;
ELOOP = 40;
EMFILE = 24;
ENAMETOOLONG = 36;
ENFILE = 23;
ENOENT = 2;
ENOSR = 63;
ENOSPC = 28;
ENOTDIR = 20;
ENXIO = 6;
EOVERFLOW = 75;
EROFS = 30;
ENOTSUP = 95;
//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 = 0x00000000;
VSTOP = 0x00000009;
VMIN = 0x00000006;
VTIME = 0x00000005;
VEOF = 0x00000004;
TIOCMGET = 0x00005415;
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 = 0x00005418;
IXON = 0x00000400;
IXOFF = 0x00001000;
IXANY = 0x00000800;
CRTSCTS = 0x80000000;
TCSADRAIN = 0x00000001;
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 = 9;
B1800 = 10;
B2400 = 11;
B4800 = 12;
B9600 = 13;
B19200 = 14;
B38400 = 15;
B57600 = 4097;
B115200 = 4098;
B230400 = 4099;
//poll.h stuff
POLLIN = 0x0001;
POLLPRI = 0x0002;
POLLOUT = 0x0004;
POLLERR = 0x0008;
POLLNVAL = 0x0020;
}
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));
}
public int cfgetospeed(Termios termios) {
return m_Clib.cfgetospeed(new termios(termios));
}
public int cfsetispeed(Termios termios, int speed) {
termios t = new termios(termios);
int ret = m_Clib.cfsetispeed(t, speed);
t.update(termios);
return ret;
}
public int cfsetospeed(Termios termios, int speed) {
termios t = new termios(termios);
int ret = m_Clib.cfsetospeed(t, 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) {
return m_Clib.ioctl(fd, cmd, data);
}
// This ioctl is Linux specific, so keep it private for now
private int ioctl(int fd, int cmd, serial_struct data) {
// Do the logging here as this does not go through the JTermios which normally does the logging
log = log && log(5, "> ioctl(%d,%d,%s)\n", fd, cmd, data);
int ret = m_Clib.ioctl(fd, cmd, data);
log = log && log(3, "< tcsetattr(%d,%d,%s) => %d\n", fd, cmd, data, ret);
return ret;
}
public String getPortNamePattern() {
// First we have to determine which serial drivers exist and which
// prefixes they use
final List prefixes = new ArrayList();
try {
BufferedReader drivers = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/tty/drivers"), "US-ASCII"));
String line;
while ((line = drivers.readLine()) != null) {
// /proc/tty/drivers contains the prefix in the second column
// and "serial" in the fifth
String[] parts = line.split(" +");
if (parts.length != 5) {
continue;
}
if (!"serial".equals(parts[4])) {
continue;
}
// Sanity check the prefix
if (!parts[1].startsWith("/dev/")) {
continue;
}
prefixes.add(parts[1].substring(5));
}
drivers.close();
} catch (IOException e) {
log = log && log(1, "failed to read /proc/tty/drivers\n");
prefixes.add("ttyS");
prefixes.add("ttyUSB");
prefixes.add("ttyACM");
}
// Now build the pattern from the known prefixes
StringBuilder pattern = new StringBuilder();
pattern.append('^');
boolean first = true;
for (String prefix : prefixes) {
if (first) {
first = false;
} else {
pattern.append('|');
}
pattern.append("(");
pattern.append(prefix);
pattern.append(".+)");
}
return pattern.toString();
}
public List 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 list = new LinkedList();
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 c = speed;
int r;
for (int i = 0; i < m_BaudRates.length; i += 2) {
if (m_BaudRates[i] == speed) {
// found the baudrate from the table
// just in case custom divisor was in use, try to turn it off first
serial_struct ss = new serial_struct();
r = ioctl(fd, TIOCGSERIAL, ss);
if (r == 0) {
ss.flags &= ~ASYNC_SPD_MASK;
r = ioctl(fd, TIOCSSERIAL, ss);
}
// now set the speed with the constant from the table
c = m_BaudRates[i + 1];
if ((r = JTermios.cfsetispeed(termios, c)) != 0) {
return r;
}
if ((r = JTermios.cfsetospeed(termios, c)) != 0) {
return r;
}
if ((r = JTermios.tcsetattr(fd, TCSANOW, termios)) != 0) {
return r;
}
return 0;
}
}
// baudrate not defined in the table, try custom divisor approach
// configure port to use custom speed instead of 38400
serial_struct ss = new serial_struct();
if ((r = ioctl(fd, TIOCGSERIAL, ss)) != 0) {
return r;
}
ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
if (speed == 0) {
log = log && log(1, "unable to set custom baudrate %d \n", speed);
return -1;
}
ss.custom_divisor = (ss.baud_base + (speed / 2)) / speed;
if (ss.custom_divisor == 0) {
log = log && log(1, "unable to set custom baudrate %d (possible division by zero)\n", speed);
return -1;
}
int closestSpeed = ss.baud_base / ss.custom_divisor;
if (closestSpeed < speed * 98 / 100 || closestSpeed > speed * 102 / 100) {
log = log && log(1, "best available baudrate %d not close enough to requested %d \n", closestSpeed, speed);
return -1;
}
if ((r = ioctl(fd, TIOCSSERIAL, ss)) != 0) {
return r;
}
if ((r = JTermios.cfsetispeed(termios, B38400)) != 0) {
return r;
}
if ((r = JTermios.cfsetospeed(termios, B38400)) != 0) {
return r;
}
if ((r = JTermios.tcsetattr(fd, TCSANOW, termios)) != 0) {
return r;
}
return 0;
}
public int pipe(int[] fds) {
return m_Clib.pipe(fds);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy