/* Copyright 2020 Dmitry Isaenko This file is part of JavaUSBTool. JavaUSBTool is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. JavaUSBTool is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JavaUSBTool. If not, see . */ package javausbtool.usb; import javafx.concurrent.Task; import javausbtool.misc.LogPrinter; import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import java.io.*; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.time.LocalTime; import java.time.format.DateTimeFormatter; class UsbLoop { private LogPrinter logPrinter; private DeviceHandle handlerNS; private Task task; private int readBufferCapacity; private long readCounter; private File saveRepliesFolder; public UsbLoop(DeviceHandle handler, int readBufferCapacity, File fileToSendOnStart, Task task, LogPrinter logPrinter, String saveRepliesTo, boolean shouldRead ){ this.handlerNS = handler; this.task = task; this.logPrinter = logPrinter; this.readBufferCapacity = readBufferCapacity; this.readCounter = 0; logPrinter.print("============= USB ============="); this.saveRepliesFolder = new File(saveRepliesTo+File.separator+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH-mm-ss"))); saveRepliesFolder.mkdirs(); logPrinter.print("Save replies to dir: "+saveRepliesFolder.getName()); if (! shouldRead) { try { writeFile(fileToSendOnStart); } catch (Exception e){ e.printStackTrace(); logPrinter.print(e.getMessage()); logPrinter.print("Terminating now"); return; } } readLoop(); } private void readLoop(){ while (true){ try { dumpData(readUsb()); } catch (InterruptedException ioe){ logPrinter.print("Execution interrupted"); return; } catch (Exception e){ e.printStackTrace(); logPrinter.print(e.getMessage()); logPrinter.print("Terminating now"); return; } } }; void writeFile(File file) throws IOException, NullPointerException, ArithmeticException { byte[] readBuffer; long currentOffset = 0; int chunk = 8388608; long size = file.length(); BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(file)); while (currentOffset < size) { if ((currentOffset + chunk) >= size) chunk = Math.toIntExact(size - currentOffset); logPrinter.updateProgress((currentOffset + chunk) / (size / 100.0) / 100.0); readBuffer = new byte[chunk]; if (bufferedInStream.read(readBuffer) != chunk) throw new IOException("Reading from file stream suddenly ended."); if (writeUsb(readBuffer)) throw new IOException("Failure during file transfer."); currentOffset += chunk; } bufferedInStream.close(); logPrinter.updateProgress(1.0); } private void dumpData(byte[] data) throws Exception{ this.readCounter++; File chunkFile = new File(saveRepliesFolder.getAbsolutePath()+File.separator+readCounter+".bin"); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(chunkFile, false)); bos.write(data); bos.close(); } /** * Sending any byte array to USB device * @return 'false' if no issues * 'true' if errors happened * */ private boolean writeUsb(byte[] message) { ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN; writeBuffer.put(message); // Don't do writeBuffer.rewind(); IntBuffer writeBufTransferred = IntBuffer.allocate(1); int result; //int varVar = 0; //todo:remove while (! task.isCancelled()) { /* if (varVar != 0) logPrinter.print("writeUsb() retry cnt: "+varVar, EMsgType.INFO); //NOTE: DEBUG varVar++; */ result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 5050); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01 switch (result){ case LibUsb.SUCCESS: if (writeBufTransferred.get() == message.length) return false; logPrinter.print("TF Data transfer issue [write]" + "\n Requested: "+message.length+ "\n Transferred: "+writeBufTransferred.get()); return true; case LibUsb.ERROR_TIMEOUT: //System.out.println("writeBuffer position: "+writeBuffer.position()+" "+writeBufTransferred.get()); //writeBufTransferred.clear(); // MUST BE HERE IF WE 'GET()' IT continue; default: logPrinter.print("TF Data transfer issue [write]" + "\n Returned: "+ UsbErrorCodes.getErrCode(result) + "\n (execution stopped)"); return true; } } logPrinter.print("INFO TF Execution interrupted"); return true; } /** * Reading what USB device responded. * @return byte array if data read successful * 'null' if read failed * */ private byte[] readUsb() throws Exception{ ByteBuffer readBuffer = ByteBuffer.allocateDirect(readBufferCapacity); // We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb. IntBuffer readBufTransferred = IntBuffer.allocate(1); int result; while (! task.isCancelled()) { result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 switch (result) { case LibUsb.SUCCESS: int trans = readBufTransferred.get(); byte[] receivedBytes = new byte[trans]; readBuffer.get(receivedBytes); return receivedBytes; case LibUsb.ERROR_TIMEOUT: continue; default: throw new Exception("Data transfer issue [read]" + "\n Returned: " + UsbErrorCodes.getErrCode(result)+ "\n (execution stopped)"); } } throw new InterruptedException("Execution interrupted"); } }