我正在尝试使用java库Libusb(org.usb4java.LibUsb)让一个小驱动程序与USB温度计进行通信 . 我正在使用vanilla linux-arm在Raspeberry Pi(3b)上测试它 .
我的问题是我没有成功将控制传输转移到设备 . 我收到错误:
org.usb4java.LibUsbException:USB错误9:控制传输失败:管道错误
这是我的代码:
主要课程:
public class usbDriver {
public static void main(String[] args) {
Communication2 com = new Communication2();
try {
com.trouverDevice();
com.preparerCom();
com.testCom();
com.terminerCom();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Communication2课程:
public class Communication2 {
/** vendor ID du thermometre */
private static final short VENDOR_ID = 0x1941;
/** product ID du thermometre */
private static final short PRODUCT_ID = (short) 0x8021;
/** interface active du thermometre */
private static final byte INTERFACE_ID = 0x0;
/** endpoint sur l'interface active du thermometre */
private static final byte ENDPOINT_ID = (byte) 0x81;
private Context contexte = null;
private Device device = null;
DeviceHandle handle = null;
private boolean pret;
private boolean detach = false;
private boolean trouve = false;
public Communication2() {
pret = false;
}
public void trouverDevice() throws SecurityException, UsbException{
// avec libUsb
// Create the libusb context
Context context = new Context();
// Initialize the libusb context
int result = LibUsb.init(context);
if (result < 0)
{
throw new LibUsbException("Unable to initialize libusb", result);
}
// Read the USB device list
DeviceList list = new DeviceList();
result = LibUsb.getDeviceList(context, list);
if (result < 0)
{
throw new LibUsbException("Unable to get device list", result);
}
try
{
// Iterate over all devices and list them
for (Device device: list)
{
int address = LibUsb.getDeviceAddress(device);
int busNumber = LibUsb.getBusNumber(device);
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result < 0)
{
throw new LibUsbException(
"Unable to read device descriptor", result);
}
if (descriptor.idVendor() == VENDOR_ID && descriptor.idProduct() == PRODUCT_ID){
System.out.println("Thermometre Pearl NC-7004 detecté !");
System.out.println(descriptor.toString());
this.device = device;
this.trouve=true;
}
}
}
finally
{
// Ensure the allocated device list is freed
//LibUsb.freeDeviceList(list, true);
}
// Deinitialize the libusb context
}
public boolean preparerCom() throws Exception{
if (!this.trouve) return false;
this.contexte = new Context();
int result = LibUsb.init(contexte);
// reclamer le handle
System.out.println("claim device handle");
this.handle = new DeviceHandle();
result = LibUsb.open(this.device, handle);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to open USB device", result);
detach = LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
detach = true; // pour forcer le claim sur le kernel
detach = detach && (LibUsb.kernelDriverActive(handle, INTERFACE_ID)==1?true:false);
System.out.println(LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER));
System.out.println((LibUsb.kernelDriverActive(handle, INTERFACE_ID)));
System.out.println(detach);
// Detach the kernel driver
if (detach)
{
System.out.println("tentative de detacher le kernel");
result = LibUsb.detachKernelDriver(handle, INTERFACE_ID);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to detach kernel driver", result);
}
detach = true;
System.out.println("claim interface");
result = LibUsb.claimInterface(handle, INTERFACE_ID);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to claim interface", result);
this.pret=false;
return this.pret;
}
public void testCom(){
if (!this.pret) return;
ByteBuffer buffer = ByteBuffer.allocate(18);
// LibUsb.fillControlSetup(buffer, (byte)0x80, (byte)0x6,
// (short)0x1, (short)0x0, (short)0x1200);
ByteBuffer buffer2 = ByteBuffer.allocateDirect(18);
int transfered = LibUsb.controlTransfer(handle,(byte)0x80,(byte)0x6,(short)0x1,(short)0x0,buffer2,2000L);
if (transfered < 0) throw new LibUsbException("Control transfer failed", transfered);
System.out.println(transfered + " bytes sent");
String test;
String test2;
if (buffer2.hasArray()) {
for(int i =0;i<buffer2.array().length;i++){
System.out.format("%02x",buffer2.array()[i]);
}
test= new String(buffer.array(),
buffer.arrayOffset() + buffer.position(),
buffer.remaining());
} else {
final byte[] b = new byte[buffer.remaining()];
buffer.duplicate().get(b);
test = new String(b);
}
System.out.println(test);
}
public void terminerCom() throws Exception{
if (this.pret){
if (this.detach)
{
int result = LibUsb.attachKernelDriver(handle, INTERFACE_ID);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to re-attach kernel driver", result);
}
LibUsb.close(this.handle);
this.trouve = false;
this.pret = false;
}
}
}
当我调用 LibUsb.ControlTransfer()
,尝试传输 GET_DESCRIPTOR
控制数据包时发生错误 . 以下是带有设备描述符信息的代码的完整返回:
> Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 0 Per Interface
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x1941
idProduct 0x8021
bcdDevice 1.00
iManufacturer 0
iProduct 0
iSerial 0
bNumConfigurations 1
claim device handle
false
0
false
claim interface
org.usb4java.LibUsbException: USB error 9: Control transfer failed: Pipe error
at usbDriver.Communication2.testCom(Communication2.java:171)
at usbDriver.usbDriver.main(usbDriver.java:36)
root@raspberrypi:/home/pi/Desktop/execUsbDriver# java -jar usbDriver_executable.jar
java.lang.IllegalArgumentException: handle must not be null
at org.usb4java.LibUsb.controlTransfer(Native Method)
at usbDriver.Communication2.testCom(Communication2.java:170)
at usbDriver.usbDriver.main(usbDriver.java:36)
我认为管道已初始化(Handle似乎没问题,界面也成功声称) . Libusb doc也指出:
如果设备不支持控制请求,则为LIBUSB_ERROR_PIPE
所以我想我只是错误地控制了控制请求 . 如果你知道正确发送 GET_DESCRIPTOR
请求的代码,我很乐意测试它!
5 回答
实际上我设法在我的设备上运行第一个请求!这只是发送请求的一个问题,这是不准确的 .
不,我想处理主要目标:从温度计中检索数据 . 使用嗅探工具,我看到我必须通过控制管道发送一个8字节变量的设置包 . 然后在 endpoints 0x81上,器件应在中断模式下发回4 * 8字节的数据 . 我用原型工具测试了它:
screen of data request
当我不明白时,我应该做同步转移 . 我现在尝试了以下代码但没有成功 .
回复如下:
你能给我一些关于如何执行此操作的建议吗?
尽管这种异步数据传输仍有很多测试 . 我完全相信我的要求很好 . 好消息是处理LibUsb.handleEventTimeOut中断传输的回调函数被调用...当它的超时到达时....
这很令人费解,因为在带有driverWizard的windows上使用的命令工作得很好 . 另外我认为应该可以使用LibUsb在Raspberry上进行当前配置,因为与设备的同步传输(获取描述符,配置...)工作正常 . 即使从未调用控件回调(可能因为设备在控制管道上没有发回任何内容),它以一个带有良好命令的TIMEOUT结束,而它是一个错误9“无效命令”,命令略有不同......它是一个好的迹象我认为,它表明设备至少对命令做出了一点反应 .
但我害怕失去理智 . 这可能是:
由于LibUsb / Raspberry导致异步传输不可操作的不兼容性?我不喜欢覆盆子! )
LibUsb处理异步传输的方式有问题吗?如果设备对命令做出一点反应,它应该在管道0x81中写入触发中断传输完成事件,不是吗?那个's exactly what is dne with the pipe listening in DriverWizard even though i don'知道它是如何完成的 .
我下周还会尝试一些尝试,但如果它没有't work i'我会忘记这个特殊的温度计 .
那么你知道温度计/湿度计可以在树莓上轻松配置并允许实时(每分钟)测量和数据传输吗?
实际上,这段代码确实有效!
@Dryman:谢谢你的建议,我认为我的主要错误是在fillControlSetup之前做fillControlTransfer . 实际上它不起作用fillControlSetup必须在fillControlTransfer之前完成(我注意到读取了synchronousControlTransfer的源代码) . 无论如何,现在我可以与设备进行有效沟通,感谢您的建议!
我认为它看起来应该更像这样:
经过多次测试和变化后,它仍然不起作用......
回调函数没有被调用真的很烦人 . 在提交controlTransfer时,应该调用sendData回调函数,不是吗?但在测试中,它从未被调用过 . 控制转移真的完成了吗?神秘......
此外,如果我做得好,USB中没有真正的听众 . 设备只有在被要求时才能说话 . 这意味着一旦控制传输完成,我应该在我的数据 endpoints 0x81上执行中断传输,数据缓冲区为32字节,但我究竟能指定设备发回数据呢?我读到了方向标记,所以是数据的第一个字节中断应该有一个特定的值,表明设备有效地写在这个缓冲区并发回数据?