From ba92bee6e67e76424832a56ea91b0bcaa4c8c184 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sat, 2 May 2020 17:30:15 +0300 Subject: [PATCH] Init --- .gitignore | 2 + JavaUSBTool.iml | 39 ++ pom.xml | 196 ++++++++ src/main/java/javausbtool/AppPreferences.java | 66 +++ src/main/java/javausbtool/JFXMain.java | 76 +++ src/main/java/javausbtool/Main.java | 26 ++ .../java/javausbtool/MediatorControl.java | 37 ++ .../controllers/LandingPageController.java | 219 +++++++++ .../java/javausbtool/misc/LogPrinter.java | 63 +++ .../javausbtool/misc/MessagesConsumer.java | 80 ++++ .../javausbtool/usb/UsbCommunications.java | 105 +++++ src/main/java/javausbtool/usb/UsbConnect.java | 219 +++++++++ .../java/javausbtool/usb/UsbErrorCodes.java | 56 +++ src/main/java/javausbtool/usb/UsbLoop.java | 202 ++++++++ src/main/resources/landingPage.fxml | 117 +++++ src/main/resources/res/NotoMono-Regular.ttf | Bin 0 -> 107848 bytes src/main/resources/res/app_icon128x128.png | Bin 0 -> 16888 bytes src/main/resources/res/app_icon32x32.png | Bin 0 -> 4381 bytes src/main/resources/res/app_icon48x48.png | Bin 0 -> 9946 bytes src/main/resources/res/app_icon64x64.png | Bin 0 -> 10364 bytes src/main/resources/res/app_light.css | 442 ++++++++++++++++++ 21 files changed, 1945 insertions(+) create mode 100644 .gitignore create mode 100644 JavaUSBTool.iml create mode 100644 pom.xml create mode 100644 src/main/java/javausbtool/AppPreferences.java create mode 100644 src/main/java/javausbtool/JFXMain.java create mode 100644 src/main/java/javausbtool/Main.java create mode 100644 src/main/java/javausbtool/MediatorControl.java create mode 100644 src/main/java/javausbtool/controllers/LandingPageController.java create mode 100644 src/main/java/javausbtool/misc/LogPrinter.java create mode 100644 src/main/java/javausbtool/misc/MessagesConsumer.java create mode 100644 src/main/java/javausbtool/usb/UsbCommunications.java create mode 100644 src/main/java/javausbtool/usb/UsbConnect.java create mode 100644 src/main/java/javausbtool/usb/UsbErrorCodes.java create mode 100644 src/main/java/javausbtool/usb/UsbLoop.java create mode 100644 src/main/resources/landingPage.fxml create mode 100644 src/main/resources/res/NotoMono-Regular.ttf create mode 100644 src/main/resources/res/app_icon128x128.png create mode 100644 src/main/resources/res/app_icon32x32.png create mode 100644 src/main/resources/res/app_icon48x48.png create mode 100644 src/main/resources/res/app_icon64x64.png create mode 100644 src/main/resources/res/app_light.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..744289d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Project exclude paths +/target/ \ No newline at end of file diff --git a/JavaUSBTool.iml b/JavaUSBTool.iml new file mode 100644 index 0000000..43d0258 --- /dev/null +++ b/JavaUSBTool.iml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..63d24ec --- /dev/null +++ b/pom.xml @@ -0,0 +1,196 @@ + + + 4.0.0 + + loper + JavaUSBTool + + javausbtool + 0.1-SNAPSHOT + + https://github.com/developersu/javausbtool/ + + Java USB Tool for prototyping and/or debug + + 2019 + + Dmitry Isaenko + https://developersu.blogspot.com/ + + + + + GNU General Public License v3 + http://www.gnu.org/licenses/gpl.txt + manual + + + + + + developer.su + Dmitry Isaenko + + Developer + + +3 + https://developersu.blogspot.com/ + + + + + UTF-8 + + + + GitHub + https://github.com/developer_su/${project.artifactId}/issues + + + + + org.openjfx + javafx-controls + 11 + linux + compile + + + org.openjfx + javafx-media + 11 + linux + compile + + + org.openjfx + javafx-fxml + 11 + linux + compile + + + org.openjfx + javafx-graphics + 11 + linux + compile + + + + org.openjfx + javafx-controls + 11 + win + compile + + + org.openjfx + javafx-media + 11 + win + compile + + + org.openjfx + javafx-fxml + 11 + win + compile + + + org.openjfx + javafx-graphics + 11 + win + compile + + + + org.openjfx + javafx-controls + 11 + mac + compile + + + org.openjfx + javafx-media + 11 + mac + compile + + + org.openjfx + javafx-fxml + 11 + mac + compile + + + org.openjfx + javafx-graphics + 11 + mac + compile + + + + org.usb4java + usb4java + 1.3.0 + compile + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + + maven-jar-plugin + 2.4 + + + default-jar + none + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + + javausbtool.Main + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + \ No newline at end of file diff --git a/src/main/java/javausbtool/AppPreferences.java b/src/main/java/javausbtool/AppPreferences.java new file mode 100644 index 0000000..7052a81 --- /dev/null +++ b/src/main/java/javausbtool/AppPreferences.java @@ -0,0 +1,66 @@ +/* + 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; + +import java.util.prefs.Preferences; + +public class AppPreferences { + private static final AppPreferences INSTANCE = new AppPreferences(); + public static AppPreferences getInstance() { return INSTANCE; } + + private Preferences preferences; + + private AppPreferences(){ + preferences = Preferences.userRoot().node("javausbtool"); + } + + public void setSaveTo(String path){preferences.put("save_to_path", path);} + public String getSaveTo(){return preferences.get("save_to_path", System.getProperty("user.dir"));} + + public String getRecentPath(){return preferences.get("recent_path", System.getProperty("user.home"));} + public void setRecentPath(String path){preferences.put("recent_path", path);} + + public double getSceneWidth(){ return preferences.getDouble("window_width", 850.0); } + public void setSceneWidth(double value){ preferences.putDouble("window_width", value); } + + public double getSceneHeight(){ return preferences.getDouble("window_height", 525.0); } + public void setSceneHeight(double value){ preferences.putDouble("window_height", value); } + + // Usb setup + public short getVid(){ return (short) preferences.getInt("vid", 0x057E); } + public void setVid(short value){ preferences.putInt("vid", value); } + + public short getPid(){ return (short) preferences.getInt("pid", 0x3000); } + public void setPid(short value){ preferences.putInt("pid", value); } + + public int getInterface(){ return preferences.getInt("interface", 1); } + public void setInterface(int value){ preferences.putInt("interface", value); } + + public int getDeviceConfiguration(){ return preferences.getInt("devconfiguration", 0); } + public void setDeviceConfiguration(int value){ preferences.putInt("devconfiguration", value); } + + public boolean getHandleKernelDrvAutoDetach(){ return preferences.getBoolean("handle_kernel_driver", true); } + public void setHandleKernelDrvAutoDetach(boolean value){preferences.putBoolean("handle_kernel_driver", value);} + + public boolean getSoftResetOnHandle(){ return preferences.getBoolean("soft_reset_on_handle", false); } + public void setSoftResetOnHandle(boolean value){preferences.putBoolean("soft_reset_on_handle", value);} + + public int getReadBufferSize(){ return preferences.getInt("read_buffer_size", 512); } + public void setReadBufferSize(int value){ preferences.putInt("read_buffer_size", value); } +} \ No newline at end of file diff --git a/src/main/java/javausbtool/JFXMain.java b/src/main/java/javausbtool/JFXMain.java new file mode 100644 index 0000000..134edc3 --- /dev/null +++ b/src/main/java/javausbtool/JFXMain.java @@ -0,0 +1,76 @@ +/* + 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; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.stage.Stage; +import javausbtool.controllers.LandingPageController; + +import java.util.Locale; +import java.util.ResourceBundle; + +public class JFXMain extends Application { + + public static final String appVersion = "v0.1"; + + @Override + public void start(Stage primaryStage) throws Exception{ + FXMLLoader loader = new FXMLLoader(getClass().getResource("/landingPage.fxml")); + + Parent root = loader.load(); + + primaryStage.getIcons().addAll( + new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")), + new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")), + new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")), + new Image(getClass().getResourceAsStream("/res/app_icon128x128.png")) + ); + + primaryStage.setTitle("JavaUSBTool "+appVersion); + primaryStage.setMinWidth(650); + primaryStage.setMinHeight(450); + Scene mainScene = new Scene(root, + AppPreferences.getInstance().getSceneWidth(), + AppPreferences.getInstance().getSceneHeight() + ); + + mainScene.getStylesheets().add("/res/app_light.css"); + + primaryStage.setScene(mainScene); + primaryStage.show(); + + LandingPageController controller = loader.getController(); + primaryStage.setOnHidden(e-> { + AppPreferences.getInstance().setSceneHeight(mainScene.getHeight()); + AppPreferences.getInstance().setSceneWidth(mainScene.getWidth()); + controller.exit(); + }); + } + + public static void main(String[] args) { + if ((args.length == 1) && (args[0].equals("-v") || args[0].equals("--version"))) + System.out.println("JavaUSBTool "+JFXMain.appVersion); + else + launch(args); + } +} diff --git a/src/main/java/javausbtool/Main.java b/src/main/java/javausbtool/Main.java new file mode 100644 index 0000000..3ce3b96 --- /dev/null +++ b/src/main/java/javausbtool/Main.java @@ -0,0 +1,26 @@ +/* + 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; + +/** + * It's a workaround to execute JavaFX application. + * */ +public class Main { + public static void main(String[] args){ JFXMain.main(args); } +} diff --git a/src/main/java/javausbtool/MediatorControl.java b/src/main/java/javausbtool/MediatorControl.java new file mode 100644 index 0000000..4b3553b --- /dev/null +++ b/src/main/java/javausbtool/MediatorControl.java @@ -0,0 +1,37 @@ +/* + 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; + +import javausbtool.controllers.LandingPageController; + +public class MediatorControl { + private LandingPageController applicationController; + + public static MediatorControl getInstance(){ + return MediatorControlHold.INSTANCE; + } + + private static class MediatorControlHold { + private static final MediatorControl INSTANCE = new MediatorControl(); + } + public void setController(LandingPageController controller){ + this.applicationController = controller; + } + public LandingPageController getContoller(){ return this.applicationController; } +} diff --git a/src/main/java/javausbtool/controllers/LandingPageController.java b/src/main/java/javausbtool/controllers/LandingPageController.java new file mode 100644 index 0000000..13b351f --- /dev/null +++ b/src/main/java/javausbtool/controllers/LandingPageController.java @@ -0,0 +1,219 @@ +/* + 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.controllers; + +import javafx.concurrent.Task; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.stage.DirectoryChooser; +import javafx.stage.FileChooser; +import javausbtool.AppPreferences; +import javausbtool.MediatorControl; +import javausbtool.usb.UsbCommunications; + +import java.io.File; +import java.net.URL; +import java.util.ResourceBundle; + +public class LandingPageController implements Initializable { + @FXML + public TextArea logArea; + @FXML + public ProgressBar progressBar; + + @FXML + private TextField vidTf, + pidTf, + devInterfaceTf, + devConfigurationTf, + readBufferSizeTf; + + @FXML + private Label saveToLbl, sendFileLbl; + + @FXML + private CheckBox handleKernelDrvDetachCb, + mandatorySoftResetCb; + + + @FXML + private Button changeSaveToBtn, fileToSendBtn, startBtn, stopBtn; + + @FXML + private RadioButton readOnStartRb, + writeOnStartRb; + + private String previouslyOpenedPath; + private File writeOnStartFile; + private Task usbCommunications; + private Thread workThread; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + MediatorControl.getInstance().setController(this); + + AppPreferences pref = AppPreferences.getInstance(); + vidTf.setText(Short.toString(pref.getVid())); + pidTf.setText(Short.toString(pref.getPid())); + devInterfaceTf.setText(Integer.toString(pref.getInterface())); + devConfigurationTf.setText(Integer.toString(pref.getDeviceConfiguration())); + handleKernelDrvDetachCb.setSelected(pref.getHandleKernelDrvAutoDetach()); + mandatorySoftResetCb.setSelected(pref.getSoftResetOnHandle()); + + readBufferSizeTf.setText(Integer.toString(pref.getReadBufferSize())); + + vidTf.setTextFormatter(getNumericTextFormatter()); + pidTf.setTextFormatter(getNumericTextFormatter()); + devConfigurationTf.setTextFormatter(getNumericTextFormatter()); + devConfigurationTf.setTextFormatter(getNumericTextFormatter()); + + readBufferSizeTf.setTextFormatter(getNumericTextFormatter()); + + saveToLbl.setText(pref.getSaveTo()); + changeSaveToBtn.setOnAction(event -> setSaveToFolder()); + + ToggleGroup readWriteToggleGrp = new ToggleGroup(); + readWriteToggleGrp.getToggles().addAll(readOnStartRb, writeOnStartRb); + readOnStartRb.setSelected(true); + + previouslyOpenedPath = System.getProperty("user.home"); + + fileToSendBtn.setOnAction(event -> selectFileToSendInitially()); + + startBtn.setOnAction(event -> startProcess()); + stopBtn.setOnAction(event -> stopProcess()); + } + + private TextFormatter getNumericTextFormatter(){ + return new TextFormatter<>(change -> { + if (change.getControlNewText().matches("^[0-9]{0,}$")) + return change; + return null; + }); + } + + private void setSaveToFolder() { + DirectoryChooser dirChooser = new DirectoryChooser(); + dirChooser.setTitle("Save files to..."); + dirChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + File dir = dirChooser.showDialog(progressBar.getScene().getWindow()); + if (dir != null && dir.exists()) + saveToLbl.setText(dir.getAbsolutePath()); + } + + private void selectFileToSendInitially(){ + File file; + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Select file"); + + File validator = new File(previouslyOpenedPath); + if (validator.exists() && validator.isDirectory()) + fileChooser.setInitialDirectory(validator); + else + return; + + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Any", "*.*")); + + file = fileChooser.showOpenDialog(sendFileLbl.getScene().getWindow()); + if (file != null) { + sendFileLbl.setText(file.getName()); + previouslyOpenedPath = file.getParent(); + writeOnStartFile = file; + } + } + + private void startProcess(){ + logArea.clear(); + if (writeOnStartRb.isSelected() && writeOnStartFile == null){ + logArea.appendText("'Write on start' option selected but no file defined\n"); + return; + } + + if ((workThread != null && workThread.isAlive())){ + logArea.appendText("Something still running\n"); + return; + } + + usbCommunications = new UsbCommunications( + (short) Integer.parseInt(vidTf.getText()), + (short) Integer.parseInt(pidTf.getText()), + Integer.parseInt(devInterfaceTf.getText()), + Integer.parseInt(devConfigurationTf.getText()), + handleKernelDrvDetachCb.isSelected(), + mandatorySoftResetCb.isSelected(), + Integer.parseInt(readBufferSizeTf.getText()), + saveToLbl.getText(), + readOnStartRb.isSelected(), + writeOnStartFile + ); + workThread = new Thread(usbCommunications); + workThread.setDaemon(true); + workThread.start(); + } + private void stopProcess(){ + if (workThread != null && workThread.isAlive()){ + usbCommunications.cancel(false); + logArea.appendText("Stop request sent\n"); + } + else + logArea.appendText("Nothing to stop\n"); + } + + /** + * Save any changes made on settings on app + * */ + public void exit() { + AppPreferences pref = AppPreferences.getInstance(); + short shortValue; + try{ + if ((shortValue = Short.parseShort(vidTf.getText())) > 0) + pref.setVid(shortValue); + } + catch (NumberFormatException e){ e.printStackTrace(); } + try{ + if ((shortValue = Short.parseShort(pidTf.getText())) > 0) + pref.setPid(shortValue); + } + catch (NumberFormatException e){ e.printStackTrace(); } + + int intValue; + try { + if ((intValue = Integer.parseInt(devInterfaceTf.getText())) > 0) + pref.setInterface(intValue); + } + catch (NumberFormatException e){ e.printStackTrace(); } + try { + if ((intValue = Integer.parseInt(devConfigurationTf.getText())) > 0) + pref.setDeviceConfiguration(intValue); + } + catch (NumberFormatException e){ e.printStackTrace(); } + + pref.setHandleKernelDrvAutoDetach(handleKernelDrvDetachCb.isSelected()); + pref.setSoftResetOnHandle(mandatorySoftResetCb.isSelected()); + + pref.setSaveTo(saveToLbl.getText()); + + try { + if ((intValue = Integer.parseInt(readBufferSizeTf.getText())) > 0) + pref.setReadBufferSize(intValue); + } + catch (NumberFormatException e){ e.printStackTrace(); } + } +} diff --git a/src/main/java/javausbtool/misc/LogPrinter.java b/src/main/java/javausbtool/misc/LogPrinter.java new file mode 100644 index 0000000..0def377 --- /dev/null +++ b/src/main/java/javausbtool/misc/LogPrinter.java @@ -0,0 +1,63 @@ +/* + 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.misc; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class LogPrinter { + private MessagesConsumer msgConsumer; + private BlockingQueue msgQueue; + private BlockingQueue progressQueue; + + public LogPrinter(){ + this.msgQueue = new LinkedBlockingQueue<>(); + this.progressQueue = new LinkedBlockingQueue<>(); + this.msgConsumer = new MessagesConsumer(this.msgQueue, this.progressQueue); + this.msgConsumer.start(); + } + /** + * This is what will print to textArea of the application. + * */ + public void print(String message){ + try { + msgQueue.put(message+"\n"); + } + catch (Exception e){ + e.printStackTrace(); + } + } + /** + * Update progress for progress bar + * */ + public void updateProgress(Double value) { + try { + progressQueue.put(value); + } + catch (InterruptedException e){ + e.printStackTrace(); + } + } + /** + * When we're done - close it + * */ + public void close(){ + msgConsumer.interrupt(); + } +} diff --git a/src/main/java/javausbtool/misc/MessagesConsumer.java b/src/main/java/javausbtool/misc/MessagesConsumer.java new file mode 100644 index 0000000..3c6f007 --- /dev/null +++ b/src/main/java/javausbtool/misc/MessagesConsumer.java @@ -0,0 +1,80 @@ +/* + 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.misc; + +import javafx.animation.AnimationTimer; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.TextArea; +import javausbtool.MediatorControl; + +import java.util.ArrayList; +import java.util.concurrent.BlockingQueue; + +public class MessagesConsumer extends AnimationTimer { + private final BlockingQueue msgQueue; + private final TextArea logsArea; + + private final BlockingQueue progressQueue; + private final ProgressBar progressBar; + + private boolean isInterrupted; + + MessagesConsumer(BlockingQueue msgQueue, BlockingQueue progressQueue){ + this.isInterrupted = false; + + this.msgQueue = msgQueue; + this.logsArea = MediatorControl.getInstance().getContoller().logArea; + + this.progressQueue = progressQueue; + this.progressBar = MediatorControl.getInstance().getContoller().progressBar; + + progressBar.setProgress(0.0); + + progressBar.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS); + } + + @Override + public void handle(long l) { + ArrayList messages = new ArrayList<>(); + int msgRecieved = msgQueue.drainTo(messages); + if (msgRecieved > 0) + messages.forEach(logsArea::appendText); + + ArrayList progress = new ArrayList<>(); + int progressRecieved = progressQueue.drainTo(progress); + if (progressRecieved > 0) { + progress.forEach(prg -> { + if (prg != 1.0) + progressBar.setProgress(prg); + else + progressBar.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS); + }); + } + + if (isInterrupted) { + progressBar.setProgress(0.0); + this.stop(); + } + } + + public void interrupt(){ + this.isInterrupted = true; + } +} \ No newline at end of file diff --git a/src/main/java/javausbtool/usb/UsbCommunications.java b/src/main/java/javausbtool/usb/UsbCommunications.java new file mode 100644 index 0000000..1eb48c9 --- /dev/null +++ b/src/main/java/javausbtool/usb/UsbCommunications.java @@ -0,0 +1,105 @@ +/* + 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 java.io.File; + +public class UsbCommunications extends Task { + + private LogPrinter logPrinter; + + private short vid; + private short pid; + private int interfaceNumber; + private int configurationNumber; + private boolean handleAutoKernelDriverDetach; + private boolean mandatoryDeviceReset; + private boolean shouldRead; + private File writeOnStartFile; + + private int readBufferCapacity; + private String saveRepliesTo; + + public UsbCommunications( + short vid, + short pid, + int interfaceNumber, + int configurationNumber, + boolean handleAutoKernelDriverDetach, + boolean mandatoryDeviceReset, + + int readBufferCapacity, + String saveRepliesTo, + boolean shouldRead, + File writeOnStartFile + ) + { + this.logPrinter = new LogPrinter(); + this.vid = vid; + this.pid = pid; + this.interfaceNumber = interfaceNumber; + this.configurationNumber = configurationNumber; + this.handleAutoKernelDriverDetach = handleAutoKernelDriverDetach; + this.mandatoryDeviceReset = mandatoryDeviceReset; + this.readBufferCapacity = readBufferCapacity; + this.saveRepliesTo = saveRepliesTo; + this.shouldRead = shouldRead; + this.writeOnStartFile = writeOnStartFile; + } + + @Override + protected Void call() { + logPrinter.print("\tStart UsbCommunications()"); + + UsbConnect usbConnect = UsbConnect.connect(logPrinter, + vid, + pid, + interfaceNumber, + configurationNumber, + handleAutoKernelDriverDetach, + mandatoryDeviceReset); + + if (! usbConnect.isConnected()){ + logPrinter.close(); + return null; + } + + DeviceHandle handler = usbConnect.getNsHandler(); + + new UsbLoop(handler, + readBufferCapacity, + writeOnStartFile, + this, + logPrinter, + saveRepliesTo, + shouldRead + ); + + usbConnect.close(); + + logPrinter.close(); + + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/javausbtool/usb/UsbConnect.java b/src/main/java/javausbtool/usb/UsbConnect.java new file mode 100644 index 0000000..8203c4f --- /dev/null +++ b/src/main/java/javausbtool/usb/UsbConnect.java @@ -0,0 +1,219 @@ +/* + 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 javausbtool.misc.LogPrinter; +import org.usb4java.*; + +public class UsbConnect { + private Context contextNS; + private DeviceHandle handlerNS; + private Device deviceNS; + + private LogPrinter logPrinter; + + private boolean connected; + + private short VENDOR_ID; + private short PRODUCT_ID; + private int INTERFACE_NUMBER; + + private int returningValue; + private DeviceList deviceList; + + public static UsbConnect connect(LogPrinter logPrinter, + short vid, + short pid, + int interfaceNumber, + int configurationNumber, + boolean handleAutoKernelDriverDetach, + boolean mandatoryDeviceReset){ + UsbConnect usbConnect = new UsbConnect(logPrinter); + usbConnect.VENDOR_ID = vid; + usbConnect.PRODUCT_ID = pid; + usbConnect.INTERFACE_NUMBER = interfaceNumber; + try { + usbConnect.createContextAndInitLibUSB(); + usbConnect.getDeviceList(); + usbConnect.findDevice(); + usbConnect.openDevice(); + usbConnect.freeDeviceList(); + if (handleAutoKernelDriverDetach) + usbConnect.setAutoDetachKernelDriver(); + if (mandatoryDeviceReset) + usbConnect.resetDevice(); + usbConnect.setConfiguration(configurationNumber); + usbConnect.claimInterface(); + usbConnect.connected = true; + } + catch (Exception e){ + logPrinter.print(e.getMessage()); + usbConnect.close(); + } + return usbConnect; + } + + private UsbConnect(){} + + private UsbConnect(LogPrinter logPrinter){ + this.logPrinter = logPrinter; + this.connected = false; + }; + + private void createContextAndInitLibUSB() throws Exception{ + // Creating Context required by libusb. Optional? Consider removing. + contextNS = new Context(); + + returningValue = LibUsb.init(contextNS); + if (returningValue != LibUsb.SUCCESS) + throw new Exception("LibUSB initialization failed: "+UsbErrorCodes.getErrCode(returningValue)); + } + + private void getDeviceList() throws Exception{ + deviceList = new DeviceList(); + returningValue = LibUsb.getDeviceList(contextNS, deviceList); + if (returningValue < 0) + throw new Exception("Can't get device list: "+UsbErrorCodes.getErrCode(returningValue)); + } + + private void findDevice() throws Exception{ + // Searching for NS in devices: looking for NS + DeviceDescriptor descriptor; + + for (Device device: deviceList){ + descriptor = getDeviceDescriptor(device); + + if ((descriptor.idVendor() == VENDOR_ID) && descriptor.idProduct() == PRODUCT_ID){ + deviceNS = device; + break; + } + } + if (deviceNS == null) { + this.freeDeviceList(); + throw new Exception("NS not found in connected USB devices"); + } + } + + private DeviceDescriptor getDeviceDescriptor(Device device) throws Exception{ + DeviceDescriptor descriptor = new DeviceDescriptor(); + + returningValue = LibUsb.getDeviceDescriptor(device, descriptor); + + if (returningValue != LibUsb.SUCCESS){ + this.freeDeviceList(); + throw new Exception("Get USB device descriptor failure: "+UsbErrorCodes.getErrCode(returningValue)); + } + return descriptor; + } + + private void openDevice() throws Exception{ + // Handle NS device + handlerNS = new DeviceHandle(); + returningValue = LibUsb.open(deviceNS, handlerNS); + + if (returningValue == LibUsb.SUCCESS) + return; + + handlerNS = null; // Avoid issue on close(); + if (returningValue == LibUsb.ERROR_ACCESS) { + throw new Exception(String.format( + "Can't open NS USB device: %s\n" + + "Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!\n\n" + + "Steps to set 'udev' rules:\n" + + "root # vim /etc/udev/rules.d/99-NS.rules\n" + + "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", GROUP=\"plugdev\"\n" + + "root # udevadm control --reload-rules && udevadm trigger\n", UsbErrorCodes.getErrCode(returningValue), VENDOR_ID, PRODUCT_ID)); + } + else + throw new Exception("Can't open NS USB device: "+UsbErrorCodes.getErrCode(returningValue)); + } + + private void freeDeviceList(){ + LibUsb.freeDeviceList(deviceList, true); + } + + private void setAutoDetachKernelDriver(){ + // Actually, there are no drivers in Linux kernel which uses this device. + returningValue = LibUsb.setAutoDetachKernelDriver(handlerNS, true); + if (returningValue != LibUsb.SUCCESS) + logPrinter.print("Skip kernel driver attach & detach ("+UsbErrorCodes.getErrCode(returningValue)+")"); + } + + private void resetDevice() throws Exception{ + returningValue = LibUsb.resetDevice(handlerNS); + if (returningValue != LibUsb.SUCCESS) + throw new Exception("Reset device\n Returned: "+UsbErrorCodes.getErrCode(returningValue)); + } + + private void setConfiguration(int configuration) throws Exception{ + returningValue = LibUsb.setConfiguration(handlerNS, configuration); + if (returningValue != LibUsb.SUCCESS) + throw new Exception("Unable to set active configuration on device: "+UsbErrorCodes.getErrCode(returningValue)); + } + private void claimInterface() throws Exception{ + // Claim interface + returningValue = LibUsb.claimInterface(handlerNS, INTERFACE_NUMBER); + if (returningValue != LibUsb.SUCCESS) + throw new Exception("Claim interface failure: "+UsbErrorCodes.getErrCode(returningValue)); + } + + /** + * Get USB status + * @return status of connection + */ + public boolean isConnected() { return connected; } + /** + * Getter for handler + * @return DeviceHandle of NS + */ + public DeviceHandle getNsHandler(){ return handlerNS; } + /** + * Getter for 'Bus ID' where NS located found + */ + public int getNsBus(){ + return LibUsb.getBusNumber(deviceNS); + } + /** + * Getter for 'Device address' where NS located at + */ + public int getNsAddress(){ + return LibUsb.getDeviceAddress(deviceNS); + } + /** + * Correct exit + * */ + public void close(){ + // Close handler in the end + if (handlerNS != null) { + // Try to release interface + returningValue = LibUsb.releaseInterface(handlerNS, INTERFACE_NUMBER); + + if (returningValue != LibUsb.SUCCESS) { + logPrinter.print("Release interface failure: " + + UsbErrorCodes.getErrCode(returningValue) + + " (sometimes it's not an issue)"); + } + + LibUsb.close(handlerNS); + } + // Close context in the end + if (contextNS != null) + LibUsb.exit(contextNS); + } +} diff --git a/src/main/java/javausbtool/usb/UsbErrorCodes.java b/src/main/java/javausbtool/usb/UsbErrorCodes.java new file mode 100644 index 0000000..9f46dc7 --- /dev/null +++ b/src/main/java/javausbtool/usb/UsbErrorCodes.java @@ -0,0 +1,56 @@ +/* + 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 org.usb4java.LibUsb; + +public class UsbErrorCodes { + public static String getErrCode(int value){ + switch (value){ + case LibUsb.ERROR_ACCESS: + return "ERROR_ACCESS"; + case LibUsb.ERROR_BUSY: + return "ERROR_BUSY"; + case LibUsb.ERROR_INTERRUPTED: + return "ERROR_INTERRUPTED"; + case LibUsb.ERROR_INVALID_PARAM: + return "ERROR_INVALID_PARAM"; + case LibUsb.ERROR_IO: + return "ERROR_IO"; + case LibUsb.ERROR_NO_DEVICE: + return "ERROR_NO_DEVICE"; + case LibUsb.ERROR_NO_MEM: + return "ERROR_NO_MEM"; + case LibUsb.ERROR_NOT_FOUND: + return "ERROR_NOT_FOUND"; + case LibUsb.ERROR_NOT_SUPPORTED: + return "ERROR_NOT_SUPPORTED"; + case LibUsb.ERROR_OTHER: + return "ERROR_OTHER"; + case LibUsb.ERROR_OVERFLOW: + return "ERROR_OVERFLOW"; + case LibUsb.ERROR_PIPE: + return "ERROR_PIPE"; + case LibUsb.ERROR_TIMEOUT: + return "ERROR_TIMEOUT"; + default: + return Integer.toString(value); + } + } +} diff --git a/src/main/java/javausbtool/usb/UsbLoop.java b/src/main/java/javausbtool/usb/UsbLoop.java new file mode 100644 index 0000000..2fa7955 --- /dev/null +++ b/src/main/java/javausbtool/usb/UsbLoop.java @@ -0,0 +1,202 @@ +/* + 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; + +/** + * Tinfoil processing + * */ +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("============= UsbProtocol ============="); + + this.saveRepliesFolder = new File(saveRepliesTo+ 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"); + } +} diff --git a/src/main/resources/landingPage.fxml b/src/main/resources/landingPage.fxml new file mode 100644 index 0000000..123ab4c --- /dev/null +++ b/src/main/resources/landingPage.fxml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +