diff --git a/src/main/java/nsusbloader/COM/USB/UsbConnect.java b/src/main/java/nsusbloader/COM/USB/UsbConnect.java index 7b17a24..4602653 100644 --- a/src/main/java/nsusbloader/COM/USB/UsbConnect.java +++ b/src/main/java/nsusbloader/COM/USB/UsbConnect.java @@ -18,6 +18,7 @@ */ package nsusbloader.COM.USB; +import nsusbloader.COM.USB.common.DeviceInformation; import nsusbloader.ModelControllers.ILogPrinter; import nsusbloader.NSLDataTypes.EMsgType; import org.usb4java.*; @@ -88,6 +89,7 @@ public static UsbConnect connectHomebrewMode(ILogPrinter logPrinter){ usbConnect.connected = true; } catch (Exception e){ + e.printStackTrace(); logPrinter.print(e.getMessage(), EMsgType.FAIL); usbConnect.close(); } @@ -193,12 +195,10 @@ private void setConfiguration(int configuration) throws Exception{ throw new Exception("Unable to set active configuration on device: "+UsbErrorCodes.getErrCode(returningValue)); } private void claimInterface() throws Exception{ - // Claim interface returningValue = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE); if (returningValue != LibUsb.SUCCESS) throw new Exception("Claim interface failure: "+UsbErrorCodes.getErrCode(returningValue)); } - /** * Get USB status * @return status of connection diff --git a/src/main/java/nsusbloader/COM/USB/common/DeviceInformation.java b/src/main/java/nsusbloader/COM/USB/common/DeviceInformation.java new file mode 100644 index 0000000..831c17a --- /dev/null +++ b/src/main/java/nsusbloader/COM/USB/common/DeviceInformation.java @@ -0,0 +1,95 @@ +/* + Copyright 2019-2020 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . +*/ +package nsusbloader.COM.USB.common; + +import nsusbloader.COM.USB.UsbErrorCodes; +import org.usb4java.*; + +import java.util.ArrayList; +import java.util.List; + +public class DeviceInformation { + private static final byte DEFAULT_IN_EP_ADDRESS = -127; // 0x81 + private static final byte DEFAULT_OUT_EP_ADDRESS = 1; + + private Device device; + private ConfigDescriptor configDescriptor; + private List interfacesInformation = new ArrayList<>(); + + private DeviceInformation(){} + + public static DeviceInformation build(DeviceHandle handler) throws Exception{ + Device device = LibUsb.getDevice(handler); + return DeviceInformation.build(device); + } + public static DeviceInformation build(Device device) throws Exception{ + DeviceInformation deviceInformation = new DeviceInformation(); + deviceInformation.device = device; + deviceInformation.claimConfigurationDescriptor(); + deviceInformation.collectInterfaces(); + deviceInformation.freeConfigurationDescriptor(); + return deviceInformation; + } + + private void claimConfigurationDescriptor() throws Exception{ + configDescriptor = new ConfigDescriptor(); + int returningValue = LibUsb.getActiveConfigDescriptor(device, configDescriptor); + + if (returningValue != LibUsb.SUCCESS) + throw new Exception("Get Active config descriptor failed: "+ UsbErrorCodes.getErrCode(returningValue)); + } + + private void collectInterfaces(){ + for (Interface intrface : configDescriptor.iface()) + interfacesInformation.add(new NsUsbInterface(intrface)); + } + + private void freeConfigurationDescriptor(){ + LibUsb.freeConfigDescriptor(configDescriptor); + } + + /** Bulk transfer endpoint IN */ + public NsUsbEndpointDescriptor getSimplifiedDefaultEndpointDescriptorIn() throws Exception{ + return getSimplifiedDefaultEndpointDescriptor(true); + } + /** Bulk transfer endpoint OUT */ + public NsUsbEndpointDescriptor getSimplifiedDefaultEndpointDescriptorOut() throws Exception{ + return getSimplifiedDefaultEndpointDescriptor(false); + } + + private NsUsbEndpointDescriptor getSimplifiedDefaultEndpointDescriptor(boolean isDescriptorIN) throws Exception{ + byte endpointAddress; + + if (isDescriptorIN) + endpointAddress = DEFAULT_IN_EP_ADDRESS; + else + endpointAddress = DEFAULT_OUT_EP_ADDRESS; + + NsUsbInterface nsUsbInterface = interfacesInformation.get(0); + + NsUsbInterfaceDescriptor firstInterfaceDescriptor = nsUsbInterface.getInterfaceDescriptors()[0]; + NsUsbEndpointDescriptor[] endpointDescriptors = firstInterfaceDescriptor.getEndpointDescriptors(); + + for (NsUsbEndpointDescriptor epDescriptor : endpointDescriptors){ + if (epDescriptor.getbEndpointAddress() == endpointAddress) + return epDescriptor; + } + throw new Exception("No "+(isDescriptorIN?"IN":"OUT")+" endpoint descriptors found on default interface"); + } +} diff --git a/src/main/java/nsusbloader/COM/USB/common/NsUsbEndpointDescriptor.java b/src/main/java/nsusbloader/COM/USB/common/NsUsbEndpointDescriptor.java new file mode 100644 index 0000000..df55248 --- /dev/null +++ b/src/main/java/nsusbloader/COM/USB/common/NsUsbEndpointDescriptor.java @@ -0,0 +1,64 @@ +/* + Copyright 2019-2020 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . +*/ +package nsusbloader.COM.USB.common; + +import org.usb4java.EndpointDescriptor; + +public class NsUsbEndpointDescriptor { + private final byte bLength; + private final byte bDescriptorType; + private final byte bEndpointAddress; + private final byte bmAttributes; + //Ignoring: Transfer Type, Synch Type, Usage Type + private final short wMaxPacketSize; + private final byte bInterval; + + NsUsbEndpointDescriptor(EndpointDescriptor endpointDescriptor){ + this.bLength = endpointDescriptor.bLength(); + this.bDescriptorType = endpointDescriptor.bDescriptorType(); + this.bEndpointAddress = endpointDescriptor.bEndpointAddress(); + this.bmAttributes = endpointDescriptor.bmAttributes(); + this.wMaxPacketSize = endpointDescriptor.wMaxPacketSize(); + this.bInterval = endpointDescriptor.bInterval(); + } + + public byte getbLength() { + return bLength; + } + + public byte getbDescriptorType() { + return bDescriptorType; + } + + public byte getbEndpointAddress() { + return bEndpointAddress; + } + + public byte getBmAttributes() { + return bmAttributes; + } + + public short getwMaxPacketSize() { + return wMaxPacketSize; + } + + public byte getbInterval() { + return bInterval; + } +} diff --git a/src/main/java/nsusbloader/COM/USB/common/NsUsbEndpointDescriptorUtils.java b/src/main/java/nsusbloader/COM/USB/common/NsUsbEndpointDescriptorUtils.java new file mode 100644 index 0000000..ee897cd --- /dev/null +++ b/src/main/java/nsusbloader/COM/USB/common/NsUsbEndpointDescriptorUtils.java @@ -0,0 +1,32 @@ +/* + Copyright 2019-2020 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . +*/ +package nsusbloader.COM.USB.common; + +import org.usb4java.EndpointDescriptor; + +public class NsUsbEndpointDescriptorUtils { + static NsUsbEndpointDescriptor[] convertFromNatives(EndpointDescriptor[] nativeEpDescriptors){ + int descriptorsCount = nativeEpDescriptors.length; + NsUsbEndpointDescriptor[] nsUsbEpDescriptors = new NsUsbEndpointDescriptor[descriptorsCount]; + for (int i = 0; i < descriptorsCount; i++) { + nsUsbEpDescriptors[i] = new NsUsbEndpointDescriptor(nativeEpDescriptors[i]); + } + return nsUsbEpDescriptors; + } +} diff --git a/src/main/java/nsusbloader/COM/USB/common/NsUsbInterface.java b/src/main/java/nsusbloader/COM/USB/common/NsUsbInterface.java new file mode 100644 index 0000000..d4be8ad --- /dev/null +++ b/src/main/java/nsusbloader/COM/USB/common/NsUsbInterface.java @@ -0,0 +1,50 @@ +/* + Copyright 2019-2020 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . +*/ +package nsusbloader.COM.USB.common; + +import org.usb4java.Interface; +import org.usb4java.InterfaceDescriptor; + +import java.util.LinkedList; + +/** + * Adapter for easier access to USB devices which has only one interface with one interface descriptor (BULK) + * + * After few JVM failed to core few 'holders' were added: such as NsUsbEndpoint descriptor and NsUsbInterfaceDescriptor + * */ + +public class NsUsbInterface { + private final Interface iface; + private final LinkedList interfaceDescriptors; + + public NsUsbInterface(Interface iface){ + this.iface = iface; + this.interfaceDescriptors = new LinkedList<>(); + collectDescriptors(); + } + + private void collectDescriptors(){ + for (InterfaceDescriptor ifaceDescriptor : iface.altsetting()){ + interfaceDescriptors.add(new NsUsbInterfaceDescriptor(ifaceDescriptor)); + } + } + public NsUsbInterfaceDescriptor[] getInterfaceDescriptors(){ + return interfaceDescriptors.toArray(new NsUsbInterfaceDescriptor[0]); + } +} diff --git a/src/main/java/nsusbloader/COM/USB/common/NsUsbInterfaceDescriptor.java b/src/main/java/nsusbloader/COM/USB/common/NsUsbInterfaceDescriptor.java new file mode 100644 index 0000000..760e157 --- /dev/null +++ b/src/main/java/nsusbloader/COM/USB/common/NsUsbInterfaceDescriptor.java @@ -0,0 +1,93 @@ +/* + Copyright 2019-2020 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . +*/ +package nsusbloader.COM.USB.common; + +import org.usb4java.EndpointDescriptor; +import org.usb4java.InterfaceDescriptor; + +import java.nio.ByteBuffer; + +public class NsUsbInterfaceDescriptor { + private final byte bLength; + private final byte bDescriptorType; + private final byte bInterfaceNumber; + private final byte bAlternateSetting; + private final byte bNumEndpoints; + private final byte bInterfaceClass; + private final byte bInterfaceSubClass; + private final byte bInterfaceProtocol; + private final byte iInterface; + //private final int extralen; + //private final ByteBuffer extra; + private final NsUsbEndpointDescriptor[] endpointDescriptors; + + NsUsbInterfaceDescriptor(InterfaceDescriptor interfaceDescriptor){ + this.bLength = interfaceDescriptor.bLength(); + this.bDescriptorType = interfaceDescriptor.bDescriptorType(); + this.bInterfaceNumber = interfaceDescriptor.bInterfaceNumber(); + this.bAlternateSetting = interfaceDescriptor.bAlternateSetting(); + this.bNumEndpoints = interfaceDescriptor.bNumEndpoints(); + this.bInterfaceClass = interfaceDescriptor.bInterfaceClass(); + this.bInterfaceSubClass = interfaceDescriptor.bInterfaceSubClass(); + this.bInterfaceProtocol = interfaceDescriptor.bInterfaceProtocol(); + this.iInterface = interfaceDescriptor.iInterface(); + + this.endpointDescriptors = NsUsbEndpointDescriptorUtils.convertFromNatives(interfaceDescriptor.endpoint()); + } + + public byte getbLength() { + return bLength; + } + + public byte getbDescriptorType() { + return bDescriptorType; + } + + public byte getbInterfaceNumber() { + return bInterfaceNumber; + } + + public byte getbAlternateSetting() { + return bAlternateSetting; + } + + public byte getbNumEndpoints() { + return bNumEndpoints; + } + + public byte getbInterfaceClass() { + return bInterfaceClass; + } + + public byte getbInterfaceSubClass() { + return bInterfaceSubClass; + } + + public byte getbInterfaceProtocol() { + return bInterfaceProtocol; + } + + public byte getiInterface() { + return iInterface; + } + + public NsUsbEndpointDescriptor[] getEndpointDescriptors() { + return endpointDescriptors; + } +} diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtTask.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtTask.java index 014f130..04b76ff 100644 --- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtTask.java +++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtTask.java @@ -50,7 +50,12 @@ public void run() { DeviceHandle handler = usbConnect.getNsHandler(); - new NxdtUsbAbi1(handler, logPrinter, saveToLocation, this); + try { + new NxdtUsbAbi1(handler, logPrinter, saveToLocation, this); + } + catch (Exception e){ + logPrinter.print(e.getMessage(), EMsgType.FAIL); + } logPrinter.print(".:: Complete ::.", EMsgType.PASS); diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java index 9e5fd9b..e51622b 100644 --- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java +++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java @@ -19,6 +19,8 @@ package nsusbloader.Utilities.nxdumptool; import nsusbloader.COM.USB.UsbErrorCodes; +import nsusbloader.COM.USB.common.DeviceInformation; +import nsusbloader.COM.USB.common.NsUsbEndpointDescriptor; import nsusbloader.ModelControllers.ILogPrinter; import nsusbloader.NSLDataTypes.EMsgType; import org.usb4java.DeviceHandle; @@ -77,14 +79,17 @@ class NxdtUsbAbi1 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - private static final long W_MAX_PACKET_SIZE = 0x200; + private short endpointMaxPacketSize; + + private static final int NXDT_USB_TIMEOUT = 5000; public NxdtUsbAbi1(DeviceHandle handler, ILogPrinter logPrinter, String saveToPath, NxdtTask parent - ){ + )throws Exception{ this.handlerNS = handler; + //this.endpointMaxPacketSize = wMaxPacketSize; this.logPrinter = logPrinter; this.parent = parent; this.isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); @@ -97,9 +102,17 @@ public NxdtUsbAbi1(DeviceHandle handler, else this.saveToPath = saveToPath; + resolveEndpointMaxPacketSize(); + readLoop(); } + private void resolveEndpointMaxPacketSize() throws Exception{ + DeviceInformation deviceInformation = DeviceInformation.build(handlerNS); + NsUsbEndpointDescriptor endpointInDescriptor = deviceInformation.getSimplifiedDefaultEndpointDescriptorIn(); + this.endpointMaxPacketSize = endpointInDescriptor.getwMaxPacketSize(); + } + private void readLoop(){ logPrinter.print("Awaiting for handshake", EMsgType.INFO); try { @@ -174,7 +187,16 @@ private void performHandshake(byte[] message) throws Exception{ writeUsb(USBSTATUS_UNSUPPORTED_ABI); throw new Exception("ABI v"+versionABI+" is not supported in current version."); } - writeUsb(USBSTATUS_SUCCESS); + replyToHandshake(); + } + private void replyToHandshake() throws Exception{ + // Send status response + endpoint max packet size + ByteBuffer buffer = ByteBuffer.allocate(USBSTATUS_SUCCESS.length + 2).order(ByteOrder.LITTLE_ENDIAN); + buffer.put(USBSTATUS_SUCCESS); + buffer.putShort(endpointMaxPacketSize); + byte[] response = buffer.array(); + + writeUsb(response); } private void handleSendFileProperties(byte[] message) throws Exception{ @@ -187,7 +209,8 @@ private void handleSendFileProperties(byte[] message) throws Exception{ logPrinter.print("Invalid filename length!", EMsgType.FAIL); return; } - + // TODO: Note, in case of a big amount of small files performace decreses dramatically. It's better to handle this only in case of 1-big-file-transfer + logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO); // If RomFs related if (isRomFs(filename)) { if (isWindows) @@ -198,7 +221,7 @@ private void handleSendFileProperties(byte[] message) throws Exception{ createPath(filename); } else { - logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO); + //logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO); // TODO: see above filename = saveToPath + filename; } @@ -222,10 +245,7 @@ private void handleSendFileProperties(byte[] message) throws Exception{ if (fileSize == 0) return; - if (isWindows10) - dumpFileOnWindowsTen(fileToDump, fileSize); - else - dumpFile(fileToDump, fileSize); + dumpFile(fileToDump, fileSize); writeUsb(USBSTATUS_SUCCESS); @@ -257,33 +277,8 @@ private void createPath(String path) throws Exception{ throw new Exception("Unable to create dir(s) for file in "+folderForTheFile); } - private void dumpFile(File file, long size) throws Exception{ - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, false))) { - byte[] readBuffer; - long received = 0; - int bufferSize; - - boolean zlt_expected = isAligned(size); - - while (received < size) { - readBuffer = readUsbFile(); - bos.write(readBuffer); - bufferSize = readBuffer.length; - received += bufferSize; - logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0); - } - - if (zlt_expected) { - logPrinter.print("Finishing with ZLT packet request", EMsgType.INFO); - readUsbFile(); - } - } finally { - logPrinter.updateProgress(1.0); - } - } - // @see https://bugs.openjdk.java.net/browse/JDK-8146538 - private void dumpFileOnWindowsTen(File file, long size) throws Exception{ + private void dumpFile(File file, long size) throws Exception{ FileOutputStream fos = new FileOutputStream(file, true); try (BufferedOutputStream bos = new BufferedOutputStream(fos)) { @@ -295,18 +290,21 @@ private void dumpFileOnWindowsTen(File file, long size) throws Exception{ boolean zlt_expected = isAligned(size); while (received < size) { - readBuffer = readUsbFile(); + //readBuffer = readUsbFile(); + readBuffer = readUsbFileDebug(); bos.write(readBuffer); - fd.sync(); // Fixes flushing under Windows (unharmful for other OS) + if (isWindows10) + fd.sync(); bufferSize = readBuffer.length; received += bufferSize; - logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0); + logPrinter.updateProgress((double)received / (double)size); } if (zlt_expected) { logPrinter.print("Finishing with ZLT packet request", EMsgType.INFO); - readUsbFile(); + //readUsbFile(); + readUsbFileDebug(); } } finally { logPrinter.updateProgress(1.0); @@ -314,7 +312,7 @@ private void dumpFileOnWindowsTen(File file, long size) throws Exception{ } /** Handle Zero-length terminator **/ private boolean isAligned(long size){ - return ((size & (W_MAX_PACKET_SIZE - 1)) == 0); + return ((size & (endpointMaxPacketSize - 1)) == 0); } /** Sending any byte array to USB device **/ @@ -326,7 +324,7 @@ private void writeUsb(byte[] message) throws Exception{ if ( parent.isCancelled() ) throw new InterruptedException("Execution interrupted"); - int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 5050); + int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, NXDT_USB_TIMEOUT); if (result == LibUsb.SUCCESS) { if (writeBufTransferred.get() == message.length) @@ -338,7 +336,6 @@ private void writeUsb(byte[] message) throws Exception{ throw new Exception("Data transfer issue [write]" + "\n Returned: " + UsbErrorCodes.getErrCode(result) + "\n (execution stopped)"); - } /** * Reading what USB device responded (command). @@ -399,4 +396,23 @@ private byte[] readUsbFile() throws Exception{ } throw new InterruptedException(); } + + private byte[] readUsbFileDebug() throws Exception { + ByteBuffer readBuffer = ByteBuffer.allocateDirect(NXDT_FILE_CHUNK_SIZE); + IntBuffer readBufTransferred = IntBuffer.allocate(1); + if (parent.isCancelled()) + throw new InterruptedException(); + + int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, NXDT_USB_TIMEOUT); + + if (result == LibUsb.SUCCESS) { + int trans = readBufTransferred.get(); + byte[] receivedBytes = new byte[trans]; + readBuffer.get(receivedBytes); + return receivedBytes; + } + throw new Exception("Data transfer issue [read file]" + + "\n Returned: " + UsbErrorCodes.getErrCode(result) + + "\n (execution stopped)"); + } }