我正在将一个 linux 应用程序移植到在 beagle 板上运行的 android 上。
我的应用程序必须访问串行端口(发送/接收)。是否可以使用应用程序读取/写入串行端口,除非 android 是“root”的?
我正在将一个 linux 应用程序移植到在 beagle 板上运行的 android 上。
我的应用程序必须访问串行端口(发送/接收)。是否可以使用应用程序读取/写入串行端口,除非 android 是“root”的?
我为 Android 编写了一个SerialPortChannel类。它使用JNA。
// Copyright 2015 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
// www.source-code.biz, www.inventec.ch/chdh
//
// This module is multi-licensed and may be used under the terms of any of the following licenses:
//
// LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html
// EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
//
// Please contact the author if you need another license.
// This module is provided "as is", without warranties of any kind.
//
// Home page: http://www.source-code.biz/snippets/java/SerialPortChannel
package biz.source_code.utils;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.LastErrorException;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
/**
* A serial port channel for Android.
*
* <p>This class uses JNA to access a serial port device (TTY) of the Android operating system.
*/
public class SerialPortChannel implements ByteChannel {
private boolean isOpen;
private int fileHandle;
private FileDescriptor fileDescriptor;
private FileInputStream rxStream;
private FileOutputStream txStream;
private FileChannel rxChannel;
private FileChannel txChannel;
/**
* Creates a serial port channel.
*
* @param fileName
* The file name of the serial port device, e.g. "/dev/ttyO2".
* @param baudRate
* The baud rate for the serial port, e.g. 9600.
* @param blocking
* <code>true</code> for blocking i/o or <code>false</code> for non-blocking i/o.
*/
public SerialPortChannel (String fileName, int baudRate, boolean blocking) throws IOException {
staticInit();
try {
open(fileName, baudRate, blocking); }
finally {
if (!isOpen) {
close2(); }}}
private void open (String fileName, int baudRate, boolean blocking) throws IOException {
int mode = LibcDefs.O_RDWR | (blocking ? 0 : LibcDefs.O_NONBLOCK);
fileHandle = libc.open(fileName, mode);
fileDescriptor = createFileDescriptor(fileHandle);
configureSerialPort(baudRate);
rxStream = new FileInputStream(fileDescriptor);
txStream = new FileOutputStream(fileDescriptor);
rxChannel = rxStream.getChannel();
txChannel = txStream.getChannel();
isOpen = true; }
private void configureSerialPort (int baudRate) throws IOException {
termios cfg = new termios();
LibcDefs.tcgetattr(fileHandle, cfg);
LibcDefs.cfmakeraw(cfg);
int baudRateCode = encodeBaudRate(baudRate);
LibcDefs.cfsetospeed(cfg, baudRateCode);
LibcDefs.tcsetattr(fileHandle, cfg); }
@Override public boolean isOpen() {
return isOpen; }
@Override public void close() throws IOException {
if (!isOpen) {
return; }
isOpen = false;
close2(); }
private void close2() throws IOException {
if (fileHandle != 0) {
libc.close(fileHandle); }
if (rxStream != null) {
rxStream.close(); }
if (txStream != null) {
txStream.close(); }}
@Override public int read (ByteBuffer buf) throws IOException {
return rxChannel.read(buf); }
public int read (byte[] buf) throws IOException {
return read(ByteBuffer.wrap(buf)); }
@Override public int write (ByteBuffer buf) throws IOException {
return txChannel.write(buf); }
public int write (byte[] buf, int pos, int len) throws IOException {
return write(ByteBuffer.wrap(buf, pos, len)); }
public int write (byte[] buf) throws IOException {
return write(buf, 0, buf.length); }
private static int encodeBaudRate (int baudRate) {
switch (baudRate) {
case 0: return 0000000;
case 50: return 0000001;
case 75: return 0000002;
case 110: return 0000003;
case 134: return 0000004;
case 150: return 0000005;
case 200: return 0000006;
case 300: return 0000007;
case 600: return 0000010;
case 1200: return 0000011;
case 1800: return 0000012;
case 2400: return 0000013;
case 4800: return 0000014;
case 9600: return 0000015;
case 19200: return 0000016;
case 38400: return 0000017;
case 57600: return 0010001;
case 115200: return 0010002;
case 230400: return 0010003;
case 460800: return 0010004;
case 500000: return 0010005;
case 576000: return 0010006;
case 921600: return 0010007;
case 1000000: return 0010010;
case 1152000: return 0010011;
case 1500000: return 0010012;
case 2000000: return 0010013;
case 2500000: return 0010014;
case 3000000: return 0010015;
case 3500000: return 0010016;
case 4000000: return 0010017;
default: throw new IllegalArgumentException("Unsupported baud rate " + baudRate + "."); }}
//------------------------------------------------------------------------------
private static Libc libc;
private static Method fileDescriptorSetInt;
private static boolean staticInitDone;
private static interface Libc extends Library {
// fcntl.h
int open(String path, int mode) throws LastErrorException;
// unistd.h
int close(int fileHandle) throws LastErrorException;
int ioctl(int fileHandle, int request, Pointer p) throws LastErrorException; }
private static class LibcDefs {
// fcntl.h
static final int O_RDWR = 00000002;
static final int O_NONBLOCK = 00004000;
// ioctls.h
static final int TCGETS = 0x5401; // for ARM and X86, Mips is 0x540d
static final int TCSETS = 0x5402;
// termios.h
static final int IGNBRK = 0000001;
static final int BRKINT = 0000002;
static final int PARMRK = 0000010;
static final int ISTRIP = 0000040;
static final int INLCR = 0000100;
static final int IGNCR = 0000200;
static final int ICRNL = 0000400;
static final int IXON = 0002000;
static final int OPOST = 0000001;
static final int ISIG = 0000001;
static final int ICANON = 0000002;
static final int ECHO = 0000010;
static final int ECHONL = 0000100;
static final int IEXTEN = 0100000;
static final int CBAUD = 0010017;
static final int CSIZE = 0000060;
static final int PARENB = 0000400;
static final int CS8 = 0000060;
private static void tcgetattr (int fileHandle, termios cfg) throws IOException {
if (libc.ioctl(fileHandle, TCGETS, cfg.getPointer()) != 0) {
throw new IOException("tcgetattr failed."); }
cfg.read(); }
private static void tcsetattr (int fileHandle, termios cfg) throws IOException {
cfg.write();
if (libc.ioctl(fileHandle, TCSETS, cfg.getPointer()) != 0) {
throw new IOException("tcsetattr failed."); }}
private static void cfmakeraw (termios cfg) {
cfg.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
cfg.c_oflag &= ~OPOST;
cfg.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
cfg.c_cflag &= ~(CSIZE|PARENB);
cfg.c_cflag |= CS8; }
private static void cfsetospeed (termios cfg, int baudRateCode) {
cfg.c_cflag = (cfg.c_cflag & ~CBAUD) | (baudRateCode & CBAUD); }}
private static 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]; // actual length depends on platform (currently 19 for ARM and X86, 23 for Mips)
@Override protected List<?> getFieldOrder() {
return Arrays.asList("c_iflag", "c_oflag", "c_cflag", "c_lflag", "c_line", "c_cc"); }}
private static synchronized void staticInit() {
if (staticInitDone) {
return; }
libc = (Libc)Native.loadLibrary("c", Libc.class);
try {
fileDescriptorSetInt = FileDescriptor.class.getDeclaredMethod("setInt$", int.class); }
catch (Exception e) {
throw new RuntimeException(e); }
staticInitDone = true; }
// This method is Android-specific and does not work with the normal Java runtime library.
private static FileDescriptor createFileDescriptor (int fileHandle) {
FileDescriptor fileDescriptor = new FileDescriptor();
try {
fileDescriptorSetInt.invoke(fileDescriptor, fileHandle); }
catch (Exception e) {
throw new RuntimeException(e); }
return fileDescriptor; }
}