diff --git a/src/cpp/_nix_based/jssc.cpp b/src/cpp/_nix_based/jssc.cpp index 634b62018..b12889b28 100644 --- a/src/cpp/_nix_based/jssc.cpp +++ b/src/cpp/_nix_based/jssc.cpp @@ -40,7 +40,12 @@ #include //Needed for select() function #endif #ifdef __APPLE__ + #include + #include + #include + #include #include //Needed for IOSSIOSPEED in Mac OS X (Non standard baudrate) + #include // Needed for MAXPATHLEN #endif #include @@ -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; +} diff --git a/src/cpp/jssc_SerialNativeInterface.h b/src/cpp/jssc_SerialNativeInterface.h index 7029b1bbe..969da1e84 100644 --- a/src/cpp/jssc_SerialNativeInterface.h +++ b/src/cpp/jssc_SerialNativeInterface.h @@ -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 diff --git a/src/java/jssc/SerialNativeInterface.java b/src/java/jssc/SerialNativeInterface.java index c5264f5e8..aba50cdf5 100644 --- a/src/java/jssc/SerialNativeInterface.java +++ b/src/java/jssc/SerialNativeInterface.java @@ -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; } @@ -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 * @@ -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); } diff --git a/src/java/jssc/SerialPortList.java b/src/java/jssc/SerialPortList.java index 5af9a95ea..f3be259cb 100644 --- a/src/java/jssc/SerialPortList.java +++ b/src/java/jssc/SerialPortList.java @@ -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; @@ -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; } @@ -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; } @@ -330,6 +334,7 @@ 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; @@ -337,6 +342,7 @@ private static String[] getUnixBasedPortNames(String searchPath, Pattern pattern else if(portHandle != SerialNativeInterface.ERR_PORT_BUSY) { serialInterface.closePort(portHandle); } + */ portsTree.add(portName); } } @@ -345,4 +351,75 @@ else if(portHandle != SerialNativeInterface.ERR_PORT_BUSY) { } return returnArray; } + + public static Map 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(); + } else { + return new HashMap(); + } + } + + public static Map getLinuxPortProperties(String portName) { + Map props = new HashMap(); + 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 getNativePortProperties(String portName) { + Map props = new HashMap(); + 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; + } } diff --git a/src/java/libs/linux/libjSSC-2.8_aarch64.so b/src/java/libs/linux/libjSSC-2.8_aarch64.so new file mode 100755 index 000000000..38813ae5f Binary files /dev/null and b/src/java/libs/linux/libjSSC-2.8_aarch64.so differ diff --git a/src/java/libs/linux/libjSSC-2.8_armhf.so b/src/java/libs/linux/libjSSC-2.8_armhf.so old mode 100644 new mode 100755 index 3742fbe08..41680a417 Binary files a/src/java/libs/linux/libjSSC-2.8_armhf.so and b/src/java/libs/linux/libjSSC-2.8_armhf.so differ diff --git a/src/java/libs/mac_os_x/libjSSC-2.8_universal.jnilib b/src/java/libs/mac_os_x/libjSSC-2.8_universal.jnilib new file mode 100755 index 000000000..012a1684b Binary files /dev/null and b/src/java/libs/mac_os_x/libjSSC-2.8_universal.jnilib differ