Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to retrieve com port properties #88

Open
wants to merge 9 commits into
base: 2.8.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions src/cpp/_nix_based/jssc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@
#include <string.h>//Needed for select() function
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/usb/USBSpec.h>
#include <serial/ioss.h>//Needed for IOSSIOSPEED in Mac OS X (Non standard baudrate)
#include <sys/param.h> // Needed for MAXPATHLEN
#endif

#include <jni.h>
Expand Down Expand Up @@ -872,3 +877,133 @@ JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getLinesStatus
env->SetIntArrayRegion(returnArray, 0, 4, returnValues);
return returnArray;
}

JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_getPortProperties
(JNIEnv *env, jclass cls, jstring portName) {
const char* portNameChar = (const char*)env->GetStringUTFChars(portName, NULL);
jclass stringClass = env->FindClass("Ljava/lang/String;");
jobjectArray ret = env->NewObjectArray(5, stringClass, NULL);

#ifdef __APPLE__

// this code is based on QtSerialPort
CFMutableDictionaryRef matching = IOServiceMatching(kIOSerialBSDServiceValue);
io_iterator_t iter = 0;
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter);
if (kr != kIOReturnSuccess) {
env->ReleaseStringUTFChars(portName, portNameChar);
return ret;
}

io_registry_entry_t service;
while ((service = IOIteratorNext(iter))) {

// compare portName against cu and tty devices
bool found = false;

CFTypeRef cu = 0;
cu = IORegistryEntrySearchCFProperty(service, kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cu) {
char buffer[MAXPATHLEN];
CFStringGetCString(CFStringRef(cu), buffer, sizeof(buffer), kCFStringEncodingUTF8);
//fprintf(stdout, "getPortProperties: %s\n", buffer);
//fflush(stdout);
if (strcmp(portNameChar, buffer) == 0) {
found = true;
}
CFRelease(cu);
}

CFTypeRef tty = 0;
tty = IORegistryEntrySearchCFProperty(service, kIOServicePlane, CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0);
if (tty) {
char buffer[MAXPATHLEN];
CFStringGetCString(CFStringRef(tty), buffer, sizeof(buffer), kCFStringEncodingUTF8);
//fprintf(stdout, "getPortProperties: %s\n", buffer);
//fflush(stdout);
if (strcmp(portNameChar, buffer) == 0) {
found = true;
}
CFRelease(tty);
}

if (!found) {
// not port we're looking for
//fprintf(stderr, "getPortProperties: %s not found", portNameChar);
//fflush(stderr);
IOObjectRelease(service);
continue;
}

io_registry_entry_t entry = service;
do {
int val = 0;
char buffer[255];

CFTypeRef idProduct = 0;
idProduct = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, CFSTR(kUSBProductID), kCFAllocatorDefault, 0);
if (idProduct && !env->GetObjectArrayElement(ret, 0)) {
CFNumberGetValue(CFNumberRef(idProduct), kCFNumberIntType, &val);
sprintf(buffer, "%04x", val);
jstring tmp = env->NewStringUTF(buffer);
env->SetObjectArrayElement(ret, 0, tmp);
env->DeleteLocalRef(tmp);
CFRelease(idProduct);
}

CFTypeRef idVendor = 0;
idVendor = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0);
if (idVendor && !env->GetObjectArrayElement(ret, 1)) {
CFNumberGetValue(CFNumberRef(idVendor), kCFNumberIntType, &val);
sprintf(buffer, "%04x", val);
jstring tmp = env->NewStringUTF(buffer);
env->SetObjectArrayElement(ret, 1, tmp);
env->DeleteLocalRef(tmp);
CFRelease(idVendor);
}

CFTypeRef manufacturer = 0;
manufacturer = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, CFSTR(kUSBVendorString), kCFAllocatorDefault, 0);
if (manufacturer && !env->GetObjectArrayElement(ret, 2)) {
CFStringGetCString(CFStringRef(manufacturer), buffer, sizeof(buffer), kCFStringEncodingUTF8);
jstring tmp = env->NewStringUTF(buffer);
env->SetObjectArrayElement(ret, 2, tmp);
env->DeleteLocalRef(tmp);
CFRelease(manufacturer);
}

CFTypeRef product = 0;
product = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, CFSTR(kUSBProductString), kCFAllocatorDefault, 0);
if (product && !env->GetObjectArrayElement(ret, 3)) {
CFStringGetCString(CFStringRef(product), buffer, sizeof(buffer), kCFStringEncodingUTF8);
jstring tmp = env->NewStringUTF(buffer);
env->SetObjectArrayElement(ret, 3, tmp);
env->DeleteLocalRef(tmp);
CFRelease(product);
}

CFTypeRef serial = 0;
serial = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
if (serial && !env->GetObjectArrayElement(ret, 4)) {
CFStringGetCString(CFStringRef(serial), buffer, sizeof(buffer), kCFStringEncodingUTF8);
jstring tmp = env->NewStringUTF(buffer);
env->SetObjectArrayElement(ret, 4, tmp);
env->DeleteLocalRef(tmp);
CFRelease(serial);
}

kr = IORegistryEntryGetParentEntry(entry, kIOServicePlane, &entry);
} while (kr == kIOReturnSuccess);

IOObjectRelease(entry);

IOObjectRelease(service);
}

IOObjectRelease(iter);

#endif // __APPLE__

env->ReleaseStringUTFChars(portName, portNameChar);
return ret;
}
8 changes: 8 additions & 0 deletions src/cpp/jssc_SerialNativeInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getLinesStatus
JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak
(JNIEnv *, jobject, jlong, jint);

/*
* Class: jssc_SerialNativeInterface
* Method: getPortProperties
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_getPortProperties
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
Expand Down
21 changes: 20 additions & 1 deletion src/java/jssc/SerialNativeInterface.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ else if(architecture.equals("arm")) {//since 2.1.0

boolean loadLib = false;

if(isLibFolderExist(libFolderPath)){
if(loadLibFromPath("jSSC-"+libVersion)) {
// nothing more to do
} else if(isLibFolderExist(libFolderPath)){
if(isLibFileExist(libFolderPath + fileSeparator + libName)){
loadLib = true;
}
Expand Down Expand Up @@ -303,6 +305,21 @@ public static String getLibraryMinorSuffix() {
*/
public static native String getNativeLibraryVersion();

/**
* Attempt to load a library using System.loadLibrary
*
* @param lib name of the library
* @return true if sucessful, false if not
*/
public static boolean loadLibFromPath(String lib) {
try {
System.loadLibrary(lib);
return true;
} catch (UnsatisfiedLinkError e) {
return false;
}
}

/**
* Open port
*
Expand Down Expand Up @@ -483,4 +500,6 @@ public static String getLibraryMinorSuffix() {
* @since 0.8
*/
public native boolean sendBreak(long handle, int duration);

public static native String[] getPortProperties(String portName);
}
81 changes: 79 additions & 2 deletions src/java/jssc/SerialPortList.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
package jssc;

import java.io.File;
import java.io.FileReader;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeSet;
import java.util.regex.Pattern;

Expand All @@ -43,7 +47,7 @@ public class SerialPortList {
serialInterface = new SerialNativeInterface();
switch (SerialNativeInterface.getOsType()) {
case SerialNativeInterface.OS_LINUX: {
PORTNAMES_REGEXP = Pattern.compile("(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}");
PORTNAMES_REGEXP = Pattern.compile("(serial|ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}");
PORTNAMES_PATH = "/dev/";
break;
}
Expand All @@ -53,7 +57,7 @@ public class SerialPortList {
break;
}
case SerialNativeInterface.OS_MAC_OS_X: {
PORTNAMES_REGEXP = Pattern.compile("tty.(serial|usbserial|usbmodem).*");
PORTNAMES_REGEXP = Pattern.compile("(cu|tty)\\..+");
PORTNAMES_PATH = "/dev/";
break;
}
Expand Down Expand Up @@ -330,13 +334,15 @@ private static String[] getUnixBasedPortNames(String searchPath, Pattern pattern
String fileName = file.getName();
if(!file.isDirectory() && !file.isFile() && pattern.matcher(fileName).find()){
String portName = searchPath + fileName;
/*
long portHandle = serialInterface.openPort(portName, false);//Open port without TIOCEXCL
if(portHandle < 0 && portHandle != SerialNativeInterface.ERR_PORT_BUSY){
continue;
}
else if(portHandle != SerialNativeInterface.ERR_PORT_BUSY) {
serialInterface.closePort(portHandle);
}
*/
portsTree.add(portName);
}
}
Expand All @@ -345,4 +351,75 @@ else if(portHandle != SerialNativeInterface.ERR_PORT_BUSY) {
}
return returnArray;
}

public static Map<String, String> getPortProperties(String portName) {
if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_LINUX) {
return getLinuxPortProperties(portName);
} else if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_MAC_OS_X) {
return getNativePortProperties(portName);
} else if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_WINDOWS){
// TODO
return new HashMap<String, String>();
} else {
return new HashMap<String, String>();
}
}

public static Map<String, String> getLinuxPortProperties(String portName) {
Map<String, String> props = new HashMap<String, String>();
try {
// portName has the format /dev/ttyUSB0
String dev = portName.split("/")[2];
File sysfsNode = new File("/sys/bus/usb-serial/devices/"+dev);

// resolve the symbolic link and store the resulting components in an array
String[] sysfsPath = sysfsNode.getCanonicalPath().split("/");

// walk the tree to the root
for (int i=sysfsPath.length-2; 0 < i; i--) {
String curPath = "/";
for (int j=1; j <= i; j++) {
curPath += sysfsPath[j]+"/";
}

// look for specific attributes
String[] attribs = { "idProduct", "idVendor", "manufacturer", "product", "serial" };
for (int j=0; j < attribs.length; j++) {
try {
Scanner in = new Scanner(new FileReader(curPath+attribs[j]));
// we treat the values just as strings
props.put(attribs[j], in.next());
} catch (Exception e) {
// ignore the attribute
}
}

// stop once we have at least one attribute
if (0 < props.size()) {
break;
}
}
} catch (Exception e) {
// nothing to do, return what we have so far
}
return props;
}

public static Map<String, String> getNativePortProperties(String portName) {
Map<String, String> props = new HashMap<String, String>();
try {
// use JNI functions to read those properties
String[] names = { "idProduct", "idVendor", "manufacturer", "product", "serial" };
String[] values = SerialNativeInterface.getPortProperties(portName);

for (int i=0; i < names.length; i++) {
if (values[i] != null) {
props.put(names[i], values[i]);
}
}
} catch (Exception e) {
// nothing to do, return what we have so far
}
return props;
}
}
Binary file added src/java/libs/linux/libjSSC-2.8_aarch64.so
Binary file not shown.
Binary file modified src/java/libs/linux/libjSSC-2.8_armhf.so
100644 → 100755
Binary file not shown.
Binary file not shown.