Using Java libusb, I've created a Java version of the C program "testLibusb.c" that is distributed in the libusb-0.1.12 release. When I created this program, TestLibUsbJava.java, I tried to stay as close as possible to the original C code in hopes that I would get the exact same results. Unfortunately, I did not. The first difference I noticed is that the Java version seemed to find the root devices of a bus while the C version did not. This is a plus, so no problem there. The next two differences occurred when specifying the -v command line argument (verbose mode). The first is that the Java version printed some values of -1 for bInterval and bInterfaceClass, while the C version printed those values as 255. I don't think this is a big deal. However, the next difference I think is more of a problem. The C program prints out the manufacturer, product, and serial number info (when available); whereas, the Java version does not. For example:
The C program prints out:
Dev #0: Logitech - Apple Optical USB Mouse
while the Java program prints out:
Dev #0: 05AC - 0306
My first thought was there was a problem with
LibusbJava.usb_get_string_simple
However, a little debugging makes me think the problem lies with the value returned by LibusbJava.usb_open. It returned the same value for all devices.
Anyway, I figure this problem is one of:
1. an error in my code,
2. an error in my Mac build of the libusbJava,
3. or an error in Java libusb
Below is my code, so if anyone can shed some light on this problem I would appreciate it.
/**
* @(#)TestLibUsbJava.java
*
*
* This software is not designed or intended for use in on-line control
* of aircraft, air traffic, aircraft navigation or aircraft
* communications; or in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import ch.ntb.usb.*;
import java.io.*;
/**
* This class replicates the code from testlibusb.c supplied in the libusb-0.1.12 release.
*/
public class TestLibUsbJava {
static boolean verbose;
/**
* prints out endpoint info
*
* @param endpoint The end point.
*/
private static void printEndpoint(Usb_Endpoint_Descriptor endpoint) {
System.out.print(String.format(" bEndpointAddress: %02xh\n", endpoint.getBEndpointAddress()));
System.out.print(String.format(" bmAttributes: %02xh\n", endpoint.getBmAttributes()));
System.out.print(String.format(" wMaxPacketSize: %d\n", endpoint.getWMaxPacketSize()));
System.out.print(String.format(" bInterval: %d\n", endpoint.getBInterval()));
System.out.print(String.format(" bRefresh: %d\n", endpoint.getBRefresh()));
System.out.print(String.format(" bSynchAddress: %d\n", endpoint.getBSynchAddress()));
}
for (Usb_Bus bus = LibusbJava.usb_get_busses(); bus != null; bus = bus.getNext()) {
if ((bus.getRootDev() != null) && !verbose) {
printDevice(bus.getRootDev(), 0);
} else {
for (Usb_Device dev = bus.getDevices(); dev != null; dev = dev.getNext()) {
printDevice(dev, 0);
}
}
}
} // end main
} // end of TestLibUsbJava class
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
What do you mean by "find the root devices of a bus"? Did the Java version display more devices than the C test program?
The second problem is a Java - C difference. Java doesn't have unsigned values. These values (bInterval and bInterfaceClass) are defined as 'unsigned char' in C which maps to byte (signed, because there is only signed byte) in Java. If used in an expression in Java it will be expanded to int. If the value is -1 that won't expand to 255 but to 0xffffffff (-1, 32 bit). So you can use 'signedByteValue && 0xff' to get the unsigned value which results in 255 (32 bit).
I ran your program on my Windows PC to see if I get any differences to the testlibusb.exe program. But it shows exactly the same information as your Java equivalent.
I don't see an obvious reason why your getting differences. You could try to insert some debug information (some printf's) in the share library to see if you really always get the same device handle on usb_open(). That should't be the case, if you're opening different devices. You could also compare the handles to the ones in the C test program. They should be the same, because the shared library does the same calls as the C program.
The values you're getting (05AC - 0306), are these the vendor and product IDs of your device? Maybe there's some offset problem in the shared library and the wrong values are read. usb_get_string_simple() is converting a C UTF8 string to a Java UTF8 string. Maybe if it gets the wrong value and this value is 0-terminated, this will just output a correct string read from a wrong offset (not really sure if that's correct).
Regards, Andreas
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
When executing the programs without the "-v" argument, I believe that
bus.getRootDev() return a non-null value in the Java version, and that
bus->root_dev returns a null (or 0) value in the C version of the program.
Output from Java version of the program
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Output from the C version of the program
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: Griffin Technology, Inc - iMic USB audio system
Dev #0: 05AC - 8005
Dev #0: Mitsumi Electric - Hub in Apple Extended USB Keyboard
Dev #0: Mitsumi Electric - Apple Extended USB Keyboard
Dev #0: Logitech - Apple Optical USB Mouse
Dev #0: 05AC - 8005
Dev #0: Keyspan, a division of InnoSys Inc. - Keyspan USA-28
---------------------------------------------------
I believe that LibusbJava.usb_open(Usb_Device dev) (see below excerpt from LibusbJava.cpp) looks-up dev's corresponding device number and then uses it to open the device and return the corresponding int. Unfortunately, all my devices return a value of 0 for their device number. This is true, using both the TestLibUsbJava.java and testlibusb.c programs, as can be seen above. Since all my devices return the same device number (0) the int returned by LibusbJava.open_usb is the same for each device. Furthermore, it must be opening the same device each time. Since the program works on your machine, it sounds like it's a Mac specific problem, and is probably a bug with libusb.
Hmm - yes, that's possibly the reason. Because the dev struct (which is used in usb_open of the C-Version) must be mapped to the Java Device object somehow, the device number (devnum) is used. If that's not correct (= 0), it will always open the same device.
What can be said for sure is, that the device numbers must be ascending or at least different from each other.
Did you compile the source yourself or do you use some binary package?
You could checkout the trunk version or build the latest release of libusb again. I hope that fixes the problem.
Regards, Andreas
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
Using Java libusb, I've created a Java version of the C program "testLibusb.c" that is distributed in the libusb-0.1.12 release. When I created this program, TestLibUsbJava.java, I tried to stay as close as possible to the original C code in hopes that I would get the exact same results. Unfortunately, I did not. The first difference I noticed is that the Java version seemed to find the root devices of a bus while the C version did not. This is a plus, so no problem there. The next two differences occurred when specifying the -v command line argument (verbose mode). The first is that the Java version printed some values of -1 for bInterval and bInterfaceClass, while the C version printed those values as 255. I don't think this is a big deal. However, the next difference I think is more of a problem. The C program prints out the manufacturer, product, and serial number info (when available); whereas, the Java version does not. For example:
The C program prints out:
Dev #0: Logitech - Apple Optical USB Mouse
while the Java program prints out:
Dev #0: 05AC - 0306
My first thought was there was a problem with
LibusbJava.usb_get_string_simple
However, a little debugging makes me think the problem lies with the value returned by LibusbJava.usb_open. It returned the same value for all devices.
Anyway, I figure this problem is one of:
1. an error in my code,
2. an error in my Mac build of the libusbJava,
3. or an error in Java libusb
Below is my code, so if anyone can shed some light on this problem I would appreciate it.
-- Mike
-----------------------------------------------------------------
/**
* @(#)TestLibUsbJava.java
*
*
* This software is not designed or intended for use in on-line control
* of aircraft, air traffic, aircraft navigation or aircraft
* communications; or in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import ch.ntb.usb.*;
import java.io.*;
/**
* This class replicates the code from testlibusb.c supplied in the libusb-0.1.12 release.
*/
public class TestLibUsbJava {
static boolean verbose;
/**
* prints out endpoint info
*
* @param endpoint The end point.
*/
private static void printEndpoint(Usb_Endpoint_Descriptor endpoint) {
System.out.print(String.format(" bEndpointAddress: %02xh\n", endpoint.getBEndpointAddress()));
System.out.print(String.format(" bmAttributes: %02xh\n", endpoint.getBmAttributes()));
System.out.print(String.format(" wMaxPacketSize: %d\n", endpoint.getWMaxPacketSize()));
System.out.print(String.format(" bInterval: %d\n", endpoint.getBInterval()));
System.out.print(String.format(" bRefresh: %d\n", endpoint.getBRefresh()));
System.out.print(String.format(" bSynchAddress: %d\n", endpoint.getBSynchAddress()));
}
/**
* prints out the interface descriptor
*
* @param interfaceDescript The interface descriptor.
*/
private static void printAltsetting(Usb_Interface_Descriptor interfaceDescript) {
System.out.print(String.format(" bInterfaceNumber: %d\n",
interfaceDescript.getBInterfaceNumber()));
System.out.print(String.format(" bAlternateSetting: %d\n",
interfaceDescript.getBAlternateSetting()));
System.out.print(String.format(" bNumEndpoints: %d\n",
interfaceDescript.getBNumEndpoints()));
System.out.print(String.format(" bInterfaceClass: %d\n",
interfaceDescript.getBInterfaceClass()));
System.out.print(String.format(" bInterfaceSubClass: %d\n",
interfaceDescript.getBInterfaceSubClass()));
System.out.print(String.format(" bInterfaceProtocol: %d\n",
interfaceDescript.getBInterfaceProtocol()));
System.out.print(String.format(" iInterface: %d\n", interfaceDescript.getIInterface()));
for (int i = 0; i < interfaceDescript.getBNumEndpoints(); i++) {
printEndpoint(interfaceDescript.getEndpoint()[i]);
}
}
/**
* prints out interface
*
* @param usbInterface The interface.
*/
private static void printInterface(Usb_Interface usbInterface) {
for (int i = 0; i < usbInterface.getNumAltsetting(); i++) {
printAltsetting(usbInterface.getAltsetting()[i]);
}
}
/**
* prints out configuration
*
* @param config The configuration.
*/
private static void printConfiguration(Usb_Config_Descriptor config) {
System.out.print(String.format(" wTotalLength: %d\n", config.getWTotalLength()));
System.out.print(String.format(" bNumInterfaces: %d\n", config.getBNumInterfaces()));
System.out.print(String.format(" bConfigurationValue: %d\n", config.getBConfigurationValue()));
System.out.print(String.format(" iConfiguration: %d\n", config.getIConfiguration()));
System.out.print(String.format(" bmAttributes: %02xh\n", config.getBmAttributes()));
System.out.print(String.format(" MaxPower: %d\n", config.getMaxPower()));
for (int i = 0; i < config.getBNumInterfaces(); i++) {
printInterface(config.getInterface()[i]);
}
}
private static int printDevice(Usb_Device dev, int level) {
int udev;
String mfr;
String product;
String sn;
String spaces;
String descript;
spaces = " ";
udev = LibusbJava.usb_open(dev);
if (udev != 0) {
if (dev.getDescriptor().getIManufacturer() != 0) {
mfr = LibusbJava.usb_get_string_simple(udev, dev.getDescriptor().getIManufacturer());
if (mfr != null) {
descript = String.format("%s - ", mfr);
} else {
descript = String.format("%04X - ", dev.getDescriptor().getIdVendor());
}
} else {
descript = String.format("%04X - ", dev.getDescriptor().getIdVendor());
}
if (dev.getDescriptor().getIProduct() != 0) {
product = LibusbJava.usb_get_string_simple(udev, dev.getDescriptor().getIProduct());
if (product != null) {
descript = descript + String.format("%s", product);
} else {
descript = descript + String.format("%04X", dev.getDescriptor().getIdProduct());
}
} else {
descript = descript + String.format("%04X", dev.getDescriptor().getIdProduct());
}
} else {
descript = String.format("%04X - %04X", dev.getDescriptor().getIdVendor(),
dev.getDescriptor().getIdProduct());
}
System.out.print(String.format("%sDev #%d: %s\n", spaces.substring(0, level * 2),
dev.getDevnum(), descript));
if ((udev != 0) && verbose) {
if (dev.getDescriptor().getISerialNumber() != 0) {
sn = LibusbJava.usb_get_string_simple(udev, dev.getDescriptor().getISerialNumber());
if (sn != null) {
System.out.print(String.format("%s - Serial Number: %s\n",
spaces.substring(0, level * 2), sn));
}
}
}
if (udev != 0) {
LibusbJava.usb_close(udev);
}
if (verbose) {
if (dev.getConfig().length == 0) {
System.out.print(" Couldn't retrieve descriptors\n");
return 0;
}
for (int i = 0; i < dev.getDescriptor().getBNumConfigurations(); i++) {
printConfiguration(dev.getConfig()[i]);
}
} else {
Usb_Device childDev = null;
for (int i = 0; i < dev.getNumChildren(); i++) {
if (i == 0) {
childDev = dev.getChildren();
} else {
childDev = childDev.getNext();
}
printDevice(childDev, level + 1);
}
}
return 0;
} // end of printDevice method
/**
* The main method.
*
* @param args The command line arguments.
*/
public static void main(String args[]) throws Exception {
if ((args.length > 0) && (args[0].equals("-v"))) {
verbose = true;
} else {
verbose = false;
}
// used for debugging. 0 = no debugging.
//
LibusbJava.usb_set_debug(0);
LibusbJava.usb_init();
LibusbJava.usb_find_busses();
LibusbJava.usb_find_devices();
for (Usb_Bus bus = LibusbJava.usb_get_busses(); bus != null; bus = bus.getNext()) {
if ((bus.getRootDev() != null) && !verbose) {
printDevice(bus.getRootDev(), 0);
} else {
for (Usb_Device dev = bus.getDevices(); dev != null; dev = dev.getNext()) {
printDevice(dev, 0);
}
}
}
} // end main
} // end of TestLibUsbJava class
Hi Mike
What do you mean by "find the root devices of a bus"? Did the Java version display more devices than the C test program?
The second problem is a Java - C difference. Java doesn't have unsigned values. These values (bInterval and bInterfaceClass) are defined as 'unsigned char' in C which maps to byte (signed, because there is only signed byte) in Java. If used in an expression in Java it will be expanded to int. If the value is -1 that won't expand to 255 but to 0xffffffff (-1, 32 bit). So you can use 'signedByteValue && 0xff' to get the unsigned value which results in 255 (32 bit).
I ran your program on my Windows PC to see if I get any differences to the testlibusb.exe program. But it shows exactly the same information as your Java equivalent.
I don't see an obvious reason why your getting differences. You could try to insert some debug information (some printf's) in the share library to see if you really always get the same device handle on usb_open(). That should't be the case, if you're opening different devices. You could also compare the handles to the ones in the C test program. They should be the same, because the shared library does the same calls as the C program.
The values you're getting (05AC - 0306), are these the vendor and product IDs of your device? Maybe there's some offset problem in the shared library and the wrong values are read. usb_get_string_simple() is converting a C UTF8 string to a Java UTF8 string. Maybe if it gets the wrong value and this value is 0-terminated, this will just output a correct string read from a wrong offset (not really sure if that's correct).
Regards, Andreas
Hi Andreas,
Thanks for help.
---------------------------------------------------
Regarding the root device:
When executing the programs without the "-v" argument, I believe that
bus.getRootDev() return a non-null value in the Java version, and that
bus->root_dev returns a null (or 0) value in the C version of the program.
Output from Java version of the program
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Output from the C version of the program
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: 05AC - 8005
Dev #0: Griffin Technology, Inc - iMic USB audio system
Dev #0: 05AC - 8005
Dev #0: Mitsumi Electric - Hub in Apple Extended USB Keyboard
Dev #0: Mitsumi Electric - Apple Extended USB Keyboard
Dev #0: Logitech - Apple Optical USB Mouse
Dev #0: 05AC - 8005
Dev #0: Keyspan, a division of InnoSys Inc. - Keyspan USA-28
---------------------------------------------------
Regarding the -1 vs 255:
I thought it was probably something like that.
---------------------------------------------------
Regarding the last problem:
I think I've homed in on the problem.
I believe that LibusbJava.usb_open(Usb_Device dev) (see below excerpt from LibusbJava.cpp) looks-up dev's corresponding device number and then uses it to open the device and return the corresponding int. Unfortunately, all my devices return a value of 0 for their device number. This is true, using both the TestLibUsbJava.java and testlibusb.c programs, as can be seen above. Since all my devices return the same device number (0) the int returned by LibusbJava.open_usb is the same for each device. Furthermore, it must be opening the same device each time. Since the program works on your machine, it sounds like it's a Mac specific problem, and is probably a bug with libusb.
Any thoughts?
-- Mike
/*
* Class: ch_ntb_usb_LibusbJava
* Method: usb_open
* Signature: (Lch/ntb/usb/Usb_Device;)I
*/
JNIEXPORT jint JNICALL Java_ch_ntb_usb_LibusbJava_usb_1open
(JNIEnv *env, jclass obj, jobject dev)
{
if (busses == NULL) { return -1; }
unsigned char devnum = env->GetByteField(dev, usb_devFID_devnum);
struct usb_bus *tmpBus;
for (tmpBus = busses; tmpBus; tmpBus = tmpBus->next) {
struct usb_device *device;
for (device = tmpBus->devices; device; device = device->next) {
if (device->devnum == devnum){
return (jint) usb_open(device);
}
}
}
return -3;
}
Hi Mike
Hmm - yes, that's possibly the reason. Because the dev struct (which is used in usb_open of the C-Version) must be mapped to the Java Device object somehow, the device number (devnum) is used. If that's not correct (= 0), it will always open the same device.
What can be said for sure is, that the device numbers must be ascending or at least different from each other.
Did you compile the source yourself or do you use some binary package?
You could checkout the trunk version or build the latest release of libusb again. I hope that fixes the problem.
Regards, Andreas