mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 20:22:17 +00:00
Get endpoint max packet size from USB host to properly enable ZLT packets when needed.
Also updated the diff patch for ns-usbloader.
This commit is contained in:
parent
f724eaa9a9
commit
dfa425790a
2 changed files with 261 additions and 116 deletions
|
@ -1,26 +1,107 @@
|
||||||
diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
|
diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
|
||||||
index 054f03a..f46fed9 100644
|
index 9e5fd9b..fcd33a3 100644
|
||||||
--- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
|
--- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
|
||||||
+++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
|
+++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
|
||||||
@@ -40,6 +40,8 @@ class NxdtUsbAbi1 {
|
@@ -22,6 +22,7 @@ import nsusbloader.COM.USB.UsbErrorCodes;
|
||||||
private boolean isWindows;
|
import nsusbloader.ModelControllers.ILogPrinter;
|
||||||
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import org.usb4java.DeviceHandle;
|
||||||
|
+import org.usb4java.DeviceDescriptor;
|
||||||
|
import org.usb4java.LibUsb;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
@@ -40,9 +41,21 @@ class NxdtUsbAbi1 {
|
||||||
|
private final boolean isWindows;
|
||||||
private boolean isWindows10;
|
private boolean isWindows10;
|
||||||
|
|
||||||
+ private BufferedOutputStream bos;
|
+ private short endpointMaxPacketSize = 0;
|
||||||
+
|
+
|
||||||
private static final int NXDT_MAX_DIRECTIVE_SIZE = 0x1000;
|
private static final int NXDT_MAX_DIRECTIVE_SIZE = 0x1000;
|
||||||
private static final int NXDT_FILE_CHUNK_SIZE = 0x800000;
|
private static final int NXDT_FILE_CHUNK_SIZE = 0x800000;
|
||||||
private static final int NXDT_FILE_PROPERTIES_MAX_NAME_LENGTH = 0x300;
|
private static final int NXDT_FILE_PROPERTIES_MAX_NAME_LENGTH = 0x300;
|
||||||
@@ -50,6 +52,8 @@ class NxdtUsbAbi1 {
|
|
||||||
private static final int CMD_HANDSHAKE = 0;
|
|
||||||
private static final int CMD_SEND_FILE_PROPERTIES = 1;
|
|
||||||
private static final int CMD_ENDSESSION = 3;
|
|
||||||
+
|
|
||||||
+ private static final int NXDT_USB_TIMEOUT = 5000;
|
+ private static final int NXDT_USB_TIMEOUT = 5000;
|
||||||
|
+
|
||||||
|
+ private static final short NXDT_USB_FS_BCD_REVISION = 0x0110;
|
||||||
|
+ private static final short NXDT_USB_FS_EP_MAX_PACKET_SIZE = 0x40;
|
||||||
|
+
|
||||||
|
+ private static final short NXDT_USB_HS_BCD_REVISION = 0x0200;
|
||||||
|
+ private static final short NXDT_USB_HS_EP_MAX_PACKET_SIZE = 0x200;
|
||||||
|
+
|
||||||
|
+ private static final short NXDT_USB_SS_BCD_REVISION = 0x0300;
|
||||||
|
+ private static final short NXDT_USB_SS_EP_MAX_PACKET_SIZE = 0x400;
|
||||||
|
|
||||||
// Standard set of possible replies
|
private static final byte ABI_VERSION = 1;
|
||||||
private static final byte[] USBSTATUS_SUCCESS = { 0x4e, 0x58, 0x44, 0x54,
|
private static final byte[] MAGIC_NXDT = { 0x4e, 0x58, 0x44, 0x54 };
|
||||||
@@ -186,6 +190,8 @@ class NxdtUsbAbi1 {
|
@@ -77,8 +90,6 @@ class NxdtUsbAbi1 {
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
|
- private static final long W_MAX_PACKET_SIZE = 0x200;
|
||||||
|
-
|
||||||
|
public NxdtUsbAbi1(DeviceHandle handler,
|
||||||
|
ILogPrinter logPrinter,
|
||||||
|
String saveToPath,
|
||||||
|
@@ -174,7 +185,58 @@ class NxdtUsbAbi1 {
|
||||||
|
writeUsb(USBSTATUS_UNSUPPORTED_ABI);
|
||||||
|
throw new Exception("ABI v"+versionABI+" is not supported in current version.");
|
||||||
|
}
|
||||||
|
- writeUsb(USBSTATUS_SUCCESS);
|
||||||
|
+
|
||||||
|
+ // Get endpoint max packet size
|
||||||
|
+ getEndpointMaxPacketSize();
|
||||||
|
+
|
||||||
|
+ // Send status response + endpoint max packet size
|
||||||
|
+ byte[] response = new byte[USBSTATUS_SUCCESS.length + 2];
|
||||||
|
+ System.arraycopy(USBSTATUS_SUCCESS, 0, response, 0, USBSTATUS_SUCCESS.length);
|
||||||
|
+ response[USBSTATUS_SUCCESS.length] = (byte)(endpointMaxPacketSize & 0xFF);
|
||||||
|
+ response[USBSTATUS_SUCCESS.length + 1] = (byte)((endpointMaxPacketSize >> 8) & 0xFF);
|
||||||
|
+
|
||||||
|
+ writeUsb(response);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private void getEndpointMaxPacketSize() throws Exception{
|
||||||
|
+ // Get device descriptor to determine the max packet size in use. We'll send it to the console.
|
||||||
|
+ // We'll get the device descriptor instead of an endpoint descriptor because it's a non-blocking call (the device descriptor is cached in memory by usb4java).
|
||||||
|
+ DeviceDescriptor deviceDescriptor = getDeviceDescriptor();
|
||||||
|
+
|
||||||
|
+ short bcdUsbRevision = deviceDescriptor.bcdUSB();
|
||||||
|
+ byte usbRevisionUpper = (byte)((bcdUsbRevision >> 8) & 0xFF);
|
||||||
|
+ byte usbRevisionLower = (byte)(bcdUsbRevision & 0xFF);
|
||||||
|
+
|
||||||
|
+ switch(bcdUsbRevision){
|
||||||
|
+ case NXDT_USB_FS_BCD_REVISION: // USB 1.1 (Full Speed).
|
||||||
|
+ endpointMaxPacketSize = NXDT_USB_FS_EP_MAX_PACKET_SIZE;
|
||||||
|
+ break;
|
||||||
|
+ case NXDT_USB_HS_BCD_REVISION: // USB 2.0 (High Speed).
|
||||||
|
+ endpointMaxPacketSize = NXDT_USB_HS_EP_MAX_PACKET_SIZE;
|
||||||
|
+ break;
|
||||||
|
+ case NXDT_USB_SS_BCD_REVISION: // USB 3.0 (Super Speed).
|
||||||
|
+ endpointMaxPacketSize = NXDT_USB_SS_EP_MAX_PACKET_SIZE;
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ writeUsb(USBSTATUS_HOSTIOERROR);
|
||||||
|
+ throw new Exception("Invalid BCD USB release number \""+usbRevisionUpper+"."+usbRevisionLower+"\" in device descriptor!");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //logPrinter.print("USB revision: "+usbRevisionUpper+"."+usbRevisionLower+".", EMsgType.INFO);
|
||||||
|
+ //logPrinter.print("Max packet size: "+endpointMaxPacketSize+" b.", EMsgType.INFO);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private DeviceDescriptor getDeviceDescriptor() throws Exception{
|
||||||
|
+ int result;
|
||||||
|
+ DeviceDescriptor descriptor = new DeviceDescriptor();
|
||||||
|
+
|
||||||
|
+ result = LibUsb.getDeviceDescriptor(LibUsb.getDevice(handlerNS), descriptor);
|
||||||
|
+ if (result != LibUsb.SUCCESS){
|
||||||
|
+ writeUsb(USBSTATUS_HOSTIOERROR);
|
||||||
|
+ throw new Exception("Failed to get device descriptor! Returned "+result+" ("+UsbErrorCodes.getErrCode(result)+").");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSendFileProperties(byte[] message) throws Exception{
|
||||||
|
@@ -188,6 +250,8 @@ class NxdtUsbAbi1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +110,7 @@ index 054f03a..f46fed9 100644
|
||||||
// If RomFs related
|
// If RomFs related
|
||||||
if (isRomFs(filename)) {
|
if (isRomFs(filename)) {
|
||||||
if (isWindows)
|
if (isWindows)
|
||||||
@@ -196,7 +202,6 @@ class NxdtUsbAbi1 {
|
@@ -198,7 +262,6 @@ class NxdtUsbAbi1 {
|
||||||
createPath(filename);
|
createPath(filename);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -37,7 +118,7 @@ index 054f03a..f46fed9 100644
|
||||||
filename = saveToPath + filename;
|
filename = saveToPath + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,13 +225,11 @@ class NxdtUsbAbi1 {
|
@@ -222,13 +285,11 @@ class NxdtUsbAbi1 {
|
||||||
if (fileSize == 0)
|
if (fileSize == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -49,21 +130,21 @@ index 054f03a..f46fed9 100644
|
||||||
|
|
||||||
writeUsb(USBSTATUS_SUCCESS);
|
writeUsb(USBSTATUS_SUCCESS);
|
||||||
|
|
||||||
+ logPrinter.print("Transfer finished!", EMsgType.INFO);
|
+ //logPrinter.print("File transfer successfully finished!", EMsgType.INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getLEint(byte[] bytes, int fromOffset){
|
private int getLEint(byte[] bytes, int fromOffset){
|
||||||
@@ -255,45 +258,43 @@ class NxdtUsbAbi1 {
|
@@ -258,63 +319,46 @@ class NxdtUsbAbi1 {
|
||||||
throw new Exception("Unable to create dir(s) for file in "+folderForTheFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- private void dumpFile(File file, long size) throws Exception{
|
private void dumpFile(File file, long size) throws Exception{
|
||||||
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, false));
|
- try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, false))) {
|
||||||
-
|
|
||||||
- byte[] readBuffer;
|
- byte[] readBuffer;
|
||||||
- long received = 0;
|
- long received = 0;
|
||||||
- int bufferSize;
|
- int bufferSize;
|
||||||
-
|
-
|
||||||
|
- boolean zlt_expected = isAligned(size);
|
||||||
|
-
|
||||||
- while (received < size) {
|
- while (received < size) {
|
||||||
- readBuffer = readUsbFile();
|
- readBuffer = readUsbFile();
|
||||||
- bos.write(readBuffer);
|
- bos.write(readBuffer);
|
||||||
|
@ -71,38 +152,48 @@ index 054f03a..f46fed9 100644
|
||||||
- received += bufferSize;
|
- received += bufferSize;
|
||||||
- logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0);
|
- logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0);
|
||||||
- }
|
- }
|
||||||
- logPrinter.updateProgress(1.0);
|
+ byte[] readBuffer;
|
||||||
- bos.close();
|
+ long received = 0;
|
||||||
+ private boolean isAligned(long size, long alignment){
|
+ int chunkSize = NXDT_FILE_CHUNK_SIZE;
|
||||||
+ return ((size & (alignment - 1)) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @see https://bugs.openjdk.java.net/browse/JDK-8146538
|
- if (zlt_expected) {
|
||||||
|
- logPrinter.print("Finishing with ZLT packet request", EMsgType.INFO);
|
||||||
|
- readUsbFile();
|
||||||
|
- }
|
||||||
|
- } finally {
|
||||||
|
- logPrinter.updateProgress(1.0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
+ boolean zltExpected = isAligned(size, endpointMaxPacketSize);
|
||||||
|
+ //logPrinter.print("ZLT packet expected: "+zlt_expected, EMsgType.INFO);
|
||||||
|
|
||||||
|
- // @see https://bugs.openjdk.java.net/browse/JDK-8146538
|
||||||
- private void dumpFileOnWindowsTen(File file, long size) throws Exception{
|
- private void dumpFileOnWindowsTen(File file, long size) throws Exception{
|
||||||
- FileOutputStream fos = new FileOutputStream(file, true);
|
- FileOutputStream fos = new FileOutputStream(file, true);
|
||||||
- BufferedOutputStream bos = new BufferedOutputStream(fos);
|
|
||||||
+ private void dumpFile(File file, long size) throws Exception{
|
|
||||||
+ FileOutputStream fos = new FileOutputStream(file, false);
|
+ FileOutputStream fos = new FileOutputStream(file, false);
|
||||||
FileDescriptor fd = fos.getFD();
|
+ FileDescriptor fd = fos.getFD();
|
||||||
+ bos = new BufferedOutputStream(fos);
|
|
||||||
|
|
||||||
byte[] readBuffer;
|
try (BufferedOutputStream bos = new BufferedOutputStream(fos)) {
|
||||||
long received = 0;
|
- FileDescriptor fd = fos.getFD();
|
||||||
|
- byte[] readBuffer;
|
||||||
|
- long received = 0;
|
||||||
- int bufferSize;
|
- int bufferSize;
|
||||||
+ int chunk_size = NXDT_FILE_CHUNK_SIZE;
|
-
|
||||||
+ boolean zlt_expected = (isAligned(size, 0x40) || isAligned(size, 0x200) || isAligned(size, 0x400)); // wMaxPacketSize
|
- boolean zlt_expected = isAligned(size);
|
||||||
+ //logPrinter.print("ZLT packet expected: "+zlt_expected, EMsgType.INFO);
|
|
||||||
+
|
|
||||||
+ while(true){
|
+ while(true){
|
||||||
+ if (!zlt_expected && received >= size) break;
|
+ // Check if we aren't expecting a ZLT packet and the whole file has already been received.
|
||||||
|
+ if (!zltExpected && received >= size) break;
|
||||||
+
|
+
|
||||||
+ if (received < size && (long)chunk_size > (size - received)) chunk_size = (int)(size - received);
|
+ // Update chunk size if needed.
|
||||||
|
+ if (received < size && (long)chunkSize > (size - received)) chunkSize = (int)(size - received);
|
||||||
+
|
+
|
||||||
+ readBuffer = readUsbFile(chunk_size);
|
+ // Read current chunk. Don't attempt to read more than we're supposed to by allocating a buffer with a size equal to the current chunk size.
|
||||||
|
+ // Immediately check if we're dealing with a ZLT packet afterwards.
|
||||||
|
+ readBuffer = readUsbFile(chunkSize);
|
||||||
+ if (readBuffer == null || readBuffer.length == 0)
|
+ if (readBuffer == null || readBuffer.length == 0)
|
||||||
+ {
|
+ {
|
||||||
+ //logPrinter.print("ZLT packet received.", EMsgType.INFO);
|
+ //logPrinter.print("ZLT packet received.", EMsgType.INFO);
|
||||||
+ if (zlt_expected && received >= size) break;
|
+ if (zltExpected && received >= size) break;
|
||||||
+ continue;
|
+ continue;
|
||||||
+ }
|
+ }
|
||||||
|
|
||||||
|
@ -112,43 +203,55 @@ index 054f03a..f46fed9 100644
|
||||||
- fd.sync(); // Fixes flushing under Windows (unharmful for other OS)
|
- fd.sync(); // Fixes flushing under Windows (unharmful for other OS)
|
||||||
- bufferSize = readBuffer.length;
|
- bufferSize = readBuffer.length;
|
||||||
- received += bufferSize;
|
- received += bufferSize;
|
||||||
+ if (isWindows10) fd.sync(); // Fixes flushing under Windows 10
|
+ if (isWindows10) fd.sync(); // Fixes flushing under Windows 10. See https://bugs.openjdk.java.net/browse/JDK-8146538
|
||||||
+ received += readBuffer.length;
|
+ received += readBuffer.length;
|
||||||
|
|
||||||
- logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0);
|
- logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0);
|
||||||
+ //logPrinter.print("Received "+readBuffer.length+" b. Got thus far: "+received+" b.", EMsgType.INFO);
|
+ //logPrinter.print("Received "+readBuffer.length+" b. Got thus far: "+received+" b.", EMsgType.INFO);
|
||||||
+ logPrinter.updateProgress((double)received / (double)size);
|
+ logPrinter.updateProgress((double)received / (double)size);
|
||||||
}
|
}
|
||||||
|
-
|
||||||
|
- if (zlt_expected) {
|
||||||
|
- logPrinter.print("Finishing with ZLT packet request", EMsgType.INFO);
|
||||||
|
- readUsbFile();
|
||||||
|
- }
|
||||||
|
- } finally {
|
||||||
- logPrinter.updateProgress(1.0);
|
- logPrinter.updateProgress(1.0);
|
||||||
bos.close();
|
}
|
||||||
|
}
|
||||||
|
/** Handle Zero-length terminator **/
|
||||||
|
- private boolean isAligned(long size){
|
||||||
|
- return ((size & (W_MAX_PACKET_SIZE - 1)) == 0);
|
||||||
|
+ private boolean isAligned(long size, long alignment){
|
||||||
|
+ return ((size & (alignment - 1)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +311,7 @@ class NxdtUsbAbi1 {
|
/** Sending any byte array to USB device **/
|
||||||
|
@@ -326,7 +370,7 @@ class NxdtUsbAbi1 {
|
||||||
if ( parent.isCancelled() )
|
if ( parent.isCancelled() )
|
||||||
throw new InterruptedException("Execution interrupted");
|
throw new InterruptedException("Execution interrupted");
|
||||||
|
|
||||||
- int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 5050);
|
- int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 5050);
|
||||||
+ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, NXDT_USB_TIMEOUT);
|
+ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, NXDT_USB_TIMEOUT);
|
||||||
|
|
||||||
switch (result){
|
if (result == LibUsb.SUCCESS) {
|
||||||
case LibUsb.SUCCESS:
|
if (writeBufTransferred.get() == message.length)
|
||||||
@@ -324,7 +325,6 @@ class NxdtUsbAbi1 {
|
@@ -338,7 +382,6 @@ class NxdtUsbAbi1 {
|
||||||
|
throw new Exception("Data transfer issue [write]" +
|
||||||
"\n Returned: " + UsbErrorCodes.getErrCode(result) +
|
"\n Returned: " + UsbErrorCodes.getErrCode(result) +
|
||||||
"\n (execution stopped)");
|
"\n (execution stopped)");
|
||||||
}
|
|
||||||
-
|
-
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Reading what USB device responded (command).
|
* Reading what USB device responded (command).
|
||||||
@@ -360,29 +360,29 @@ class NxdtUsbAbi1 {
|
@@ -374,29 +417,25 @@ class NxdtUsbAbi1 {
|
||||||
* @return byte array if data read successful
|
* @return byte array if data read successful
|
||||||
* 'null' if read failed
|
* 'null' if read failed
|
||||||
* */
|
* */
|
||||||
- private byte[] readUsbFile() throws Exception{
|
- private byte[] readUsbFile() throws Exception{
|
||||||
- ByteBuffer readBuffer = ByteBuffer.allocateDirect(NXDT_FILE_CHUNK_SIZE);
|
- ByteBuffer readBuffer = ByteBuffer.allocateDirect(NXDT_FILE_CHUNK_SIZE);
|
||||||
+ private byte[] readUsbFile(int chunk_size) throws Exception{
|
+ private byte[] readUsbFile(int chunkSize) throws Exception{
|
||||||
+ ByteBuffer readBuffer = ByteBuffer.allocateDirect(chunk_size);
|
+ ByteBuffer readBuffer = ByteBuffer.allocateDirect(chunkSize);
|
||||||
IntBuffer readBufTransferred = IntBuffer.allocate(1);
|
IntBuffer readBufTransferred = IntBuffer.allocate(1);
|
||||||
- int result;
|
- int result;
|
||||||
- int countDown = 0;
|
- int countDown = 0;
|
||||||
|
@ -170,10 +273,7 @@ index 054f03a..f46fed9 100644
|
||||||
- "\n (execution stopped)");
|
- "\n (execution stopped)");
|
||||||
- }
|
- }
|
||||||
+ if ( parent.isCancelled() )
|
+ if ( parent.isCancelled() )
|
||||||
+ {
|
|
||||||
+ bos.close();
|
|
||||||
+ throw new InterruptedException();
|
+ throw new InterruptedException();
|
||||||
+ }
|
|
||||||
+
|
+
|
||||||
+ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, NXDT_USB_TIMEOUT);
|
+ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, NXDT_USB_TIMEOUT);
|
||||||
+
|
+
|
||||||
|
@ -184,7 +284,6 @@ index 054f03a..f46fed9 100644
|
||||||
+ readBuffer.get(receivedBytes);
|
+ readBuffer.get(receivedBytes);
|
||||||
+ return receivedBytes;
|
+ return receivedBytes;
|
||||||
+ default:
|
+ default:
|
||||||
+ bos.close();
|
|
||||||
+ throw new Exception("Data transfer issue [read file]" +
|
+ throw new Exception("Data transfer issue [read file]" +
|
||||||
+ "\n Returned: " + UsbErrorCodes.getErrCode(result)+
|
+ "\n Returned: " + UsbErrorCodes.getErrCode(result)+
|
||||||
+ "\n (execution stopped)");
|
+ "\n (execution stopped)");
|
96
source/usb.c
96
source/usb.c
|
@ -31,6 +31,14 @@
|
||||||
#define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB. */
|
#define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB. */
|
||||||
#define USB_TRANSFER_TIMEOUT 5 /* 5 seconds. */
|
#define USB_TRANSFER_TIMEOUT 5 /* 5 seconds. */
|
||||||
|
|
||||||
|
#define USB_FS_BCD_REVISION 0x0110
|
||||||
|
#define USB_FS_EP_MAX_PACKET_SIZE 0x40
|
||||||
|
|
||||||
|
#define USB_HS_BCD_REVISION 0x0200
|
||||||
|
#define USB_HS_EP_MAX_PACKET_SIZE 0x200
|
||||||
|
|
||||||
|
#define USB_SS_BCD_REVISION 0x0300
|
||||||
|
#define USB_SS_EP_MAX_PACKET_SIZE 0x400
|
||||||
/* Type definitions. */
|
/* Type definitions. */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -108,6 +116,7 @@ static atomic_bool g_usbDetectionThreadCreated = false;
|
||||||
static u8 *g_usbTransferBuffer = NULL;
|
static u8 *g_usbTransferBuffer = NULL;
|
||||||
static u64 g_usbTransferRemainingSize = 0, g_usbTransferWrittenSize = 0;
|
static u64 g_usbTransferRemainingSize = 0, g_usbTransferWrittenSize = 0;
|
||||||
static u32 g_usbUrbId = 0;
|
static u32 g_usbUrbId = 0;
|
||||||
|
static u16 g_usbEndpointMaxPacketSize = 0;
|
||||||
|
|
||||||
/* Function prototypes. */
|
/* Function prototypes. */
|
||||||
|
|
||||||
|
@ -117,6 +126,7 @@ static int usbDetectionThreadFunc(void *arg);
|
||||||
|
|
||||||
static bool usbStartSession(void);
|
static bool usbStartSession(void);
|
||||||
static void usbEndSession(void);
|
static void usbEndSession(void);
|
||||||
|
static bool usbGetMaxPacketSizeFromHost(void);
|
||||||
|
|
||||||
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size);
|
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size);
|
||||||
static u32 usbSendCommand(size_t cmd_size);
|
static u32 usbSendCommand(size_t cmd_size);
|
||||||
|
@ -304,8 +314,8 @@ bool usbSendFileData(void *data, u64 data_size)
|
||||||
/* First, check if this is the last data chunk for this file. */
|
/* First, check if this is the last data chunk for this file. */
|
||||||
if ((g_usbTransferRemainingSize - data_size) == 0)
|
if ((g_usbTransferRemainingSize - data_size) == 0)
|
||||||
{
|
{
|
||||||
/* Enable ZLT if the last chunk size is aligned to the USB max packet size. */
|
/* Enable ZLT if the last chunk size is aligned to the USB endpoint max packet size. */
|
||||||
if (IS_ALIGNED(data_size, 0x40) || IS_ALIGNED(data_size, 0x200) || IS_ALIGNED(data_size, 0x400)) /* USB1, USB2 and USB3 max packet sizes, respectively. */
|
if (IS_ALIGNED(data_size, g_usbEndpointMaxPacketSize))
|
||||||
{
|
{
|
||||||
zlt_required = true;
|
zlt_required = true;
|
||||||
usbSetZltPacket(true);
|
usbSetZltPacket(true);
|
||||||
|
@ -335,9 +345,6 @@ bool usbSendFileData(void *data, u64 data_size)
|
||||||
/* Check if this is the last chunk. */
|
/* Check if this is the last chunk. */
|
||||||
if (!g_usbTransferRemainingSize)
|
if (!g_usbTransferRemainingSize)
|
||||||
{
|
{
|
||||||
/* Disable ZLT if it was previously enabled. */
|
|
||||||
if (zlt_required) usbSetZltPacket(false);
|
|
||||||
|
|
||||||
/* Check response from host device. */
|
/* Check response from host device. */
|
||||||
if (!usbRead(g_usbTransferBuffer, sizeof(UsbStatus), true))
|
if (!usbRead(g_usbTransferBuffer, sizeof(UsbStatus), true))
|
||||||
{
|
{
|
||||||
|
@ -360,6 +367,10 @@ bool usbSendFileData(void *data, u64 data_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
/* Disable ZLT if it was previously enabled. */
|
||||||
|
if (zlt_required) usbSetZltPacket(false);
|
||||||
|
|
||||||
|
/* Reset remaining and written sizes in case of errors. */
|
||||||
if (!ret) g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
if (!ret) g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
||||||
|
|
||||||
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
||||||
|
@ -441,16 +452,20 @@ static int usbDetectionThreadFunc(void *arg)
|
||||||
g_usbHostAvailable = usbIsHostAvailable();
|
g_usbHostAvailable = usbIsHostAvailable();
|
||||||
g_usbSessionStarted = false;
|
g_usbSessionStarted = false;
|
||||||
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
||||||
|
g_usbEndpointMaxPacketSize = 0;
|
||||||
|
|
||||||
/* Start an USB session if we're connected to a host device. */
|
/* Start an USB session if we're connected to a host device. */
|
||||||
/* This will essentially hang this thread and all other threads that call USB-related functions until: */
|
/* This will essentially hang this thread and all other threads that call USB-related functions until: */
|
||||||
/* a) A session is established. */
|
/* a) A session is successfully established. */
|
||||||
/* b) The console is disconnected. */
|
/* b) The console is disconnected from the USB host. */
|
||||||
/* c) The thread exit event is triggered. */
|
/* c) The thread exit event is triggered. */
|
||||||
if (g_usbHostAvailable)
|
if (g_usbHostAvailable)
|
||||||
{
|
{
|
||||||
/* Wait until a session is established. */
|
/* Wait until a session is established. */
|
||||||
g_usbSessionStarted = usbStartSession();
|
/* If the session is successfully established, then we'll get the endpoint max packet size from the data chunk sent by the USB host. */
|
||||||
|
/* This is done to accurately know when and where to enable Zero Length Termination (ZLT) packets during bulk transfers. */
|
||||||
|
/* As much as I'd like to avoid this, usb:ds doesn't disclose information such as the exact device descriptor and/or speed used by the USB host. */
|
||||||
|
g_usbSessionStarted = (usbStartSession() && usbGetMaxPacketSizeFromHost());
|
||||||
|
|
||||||
/* Check if the exit event was triggered while waiting for a session to be established. */
|
/* Check if the exit event was triggered while waiting for a session to be established. */
|
||||||
if (!g_usbSessionStarted && g_usbDetectionThreadExitFlag) break;
|
if (!g_usbSessionStarted && g_usbDetectionThreadExitFlag) break;
|
||||||
|
@ -464,6 +479,7 @@ static int usbDetectionThreadFunc(void *arg)
|
||||||
if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession();
|
if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession();
|
||||||
g_usbHostAvailable = g_usbSessionStarted = g_usbDetectionThreadExitFlag = false;
|
g_usbHostAvailable = g_usbSessionStarted = g_usbDetectionThreadExitFlag = false;
|
||||||
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
||||||
|
g_usbEndpointMaxPacketSize = 0;
|
||||||
|
|
||||||
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
||||||
rwlockWriteUnlock(&g_usbDeviceLock);
|
rwlockWriteUnlock(&g_usbDeviceLock);
|
||||||
|
@ -514,6 +530,32 @@ static void usbEndSession(void)
|
||||||
if (!usbWrite(g_usbTransferBuffer, sizeof(UsbCommandHeader), true)) LOGFILE("Failed to send EndSession command!");
|
if (!usbWrite(g_usbTransferBuffer, sizeof(UsbCommandHeader), true)) LOGFILE("Failed to send EndSession command!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool usbGetMaxPacketSizeFromHost(void)
|
||||||
|
{
|
||||||
|
/* Get the endpoint max packet size from the data chunk sent by the USB host. */
|
||||||
|
g_usbEndpointMaxPacketSize = *((u16*)(g_usbTransferBuffer + sizeof(UsbStatus)));
|
||||||
|
|
||||||
|
/* Verify the max packet size value. */
|
||||||
|
if (g_usbEndpointMaxPacketSize != USB_FS_EP_MAX_PACKET_SIZE && g_usbEndpointMaxPacketSize != USB_HS_EP_MAX_PACKET_SIZE && g_usbEndpointMaxPacketSize != USB_SS_EP_MAX_PACKET_SIZE)
|
||||||
|
{
|
||||||
|
/* Stall input (write) endpoint. */
|
||||||
|
/* This will force the client to stop the current session, so a new one will have to be established. */
|
||||||
|
rwlockWriteLock(&(g_usbDeviceInterface.lock_in));
|
||||||
|
usbDsEndpoint_Stall(g_usbDeviceInterface.endpoint_in);
|
||||||
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in));
|
||||||
|
|
||||||
|
/* Reset updated variables. */
|
||||||
|
g_usbSessionStarted = false;
|
||||||
|
g_usbEndpointMaxPacketSize = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGFILE("USB session successfully established. Endpoint max packet size: 0x%04X.", g_usbEndpointMaxPacketSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size)
|
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size)
|
||||||
{
|
{
|
||||||
if (cmd > UsbCommandType_EndSession) return;
|
if (cmd > UsbCommandType_EndSession) return;
|
||||||
|
@ -542,7 +584,11 @@ static u32 usbSendCommand(size_t cmd_size)
|
||||||
return UsbStatusType_WriteCommandFailed;
|
return UsbStatusType_WriteCommandFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!usbRead(g_usbTransferBuffer, sizeof(UsbStatus), true))
|
/* Make sure to read the USB endpoint max packet size being used by the host if this is a StartSession command. It must be part of the response after the UsbStatus block. */
|
||||||
|
u64 read_size = sizeof(UsbStatus);
|
||||||
|
if (cmd == UsbCommandType_StartSession) read_size += sizeof(g_usbEndpointMaxPacketSize);
|
||||||
|
|
||||||
|
if (!usbRead(g_usbTransferBuffer, read_size, true))
|
||||||
{
|
{
|
||||||
/* Log error message only if the USB session has been started, or if thread exit flag hasn't been enabled. */
|
/* Log error message only if the USB session has been started, or if thread exit flag hasn't been enabled. */
|
||||||
if (g_usbSessionStarted || !g_usbDetectionThreadExitFlag) LOGFILE("Failed to read 0x%lX bytes long status block for type 0x%X command!", sizeof(UsbStatus), cmd);
|
if (g_usbSessionStarted || !g_usbDetectionThreadExitFlag) LOGFILE("Failed to read 0x%lX bytes long status block for type 0x%X command!", sizeof(UsbStatus), cmd);
|
||||||
|
@ -623,36 +669,36 @@ static bool usbInitializeComms(void)
|
||||||
u8 manufacturer = 0, product = 0, serial_number = 0;
|
u8 manufacturer = 0, product = 0, serial_number = 0;
|
||||||
static const u16 supported_langs[1] = { 0x0409 };
|
static const u16 supported_langs[1] = { 0x0409 };
|
||||||
|
|
||||||
/* Send language descriptor. */
|
/* Set language. */
|
||||||
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs) / sizeof(u16));
|
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs) / sizeof(u16));
|
||||||
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbLanguageStringDescriptor failed! (0x%08X).", rc);
|
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbLanguageStringDescriptor failed! (0x%08X).", rc);
|
||||||
|
|
||||||
/* Send manufacturer. */
|
/* Set manufacturer. */
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
rc = usbDsAddUsbStringDescriptor(&manufacturer, APP_AUTHOR);
|
rc = usbDsAddUsbStringDescriptor(&manufacturer, APP_AUTHOR);
|
||||||
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (manufacturer).", rc);
|
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (manufacturer).", rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send product. */
|
/* Set product. */
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
rc = usbDsAddUsbStringDescriptor(&product, APP_TITLE);
|
rc = usbDsAddUsbStringDescriptor(&product, APP_TITLE);
|
||||||
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (product).", rc);
|
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (product).", rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send serial number. */
|
/* Set serial number. */
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
rc = usbDsAddUsbStringDescriptor(&serial_number, APP_VERSION);
|
rc = usbDsAddUsbStringDescriptor(&serial_number, APP_VERSION);
|
||||||
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (serial number).", rc);
|
if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (serial number).", rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send device descriptors. */
|
/* Set device descriptors. */
|
||||||
struct usb_device_descriptor device_descriptor = {
|
struct usb_device_descriptor device_descriptor = {
|
||||||
.bLength = USB_DT_DEVICE_SIZE,
|
.bLength = USB_DT_DEVICE_SIZE,
|
||||||
.bDescriptorType = USB_DT_DEVICE,
|
.bDescriptorType = USB_DT_DEVICE,
|
||||||
.bcdUSB = 0x0110,
|
.bcdUSB = USB_FS_BCD_REVISION,
|
||||||
.bDeviceClass = 0x00,
|
.bDeviceClass = 0x00,
|
||||||
.bDeviceSubClass = 0x00,
|
.bDeviceSubClass = 0x00,
|
||||||
.bDeviceProtocol = 0x00,
|
.bDeviceProtocol = 0x00,
|
||||||
|
@ -674,7 +720,7 @@ static bool usbInitializeComms(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* High Speed is USB 2.0. */
|
/* High Speed is USB 2.0. */
|
||||||
device_descriptor.bcdUSB = 0x0200;
|
device_descriptor.bcdUSB = USB_HS_BCD_REVISION;
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
|
rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
|
||||||
|
@ -683,7 +729,7 @@ static bool usbInitializeComms(void)
|
||||||
|
|
||||||
/* Super Speed is USB 3.0. */
|
/* Super Speed is USB 3.0. */
|
||||||
/* Upgrade packet size to 512 (1 << 9). */
|
/* Upgrade packet size to 512 (1 << 9). */
|
||||||
device_descriptor.bcdUSB = 0x0300;
|
device_descriptor.bcdUSB = USB_SS_BCD_REVISION;
|
||||||
device_descriptor.bMaxPacketSize0 = 0x09;
|
device_descriptor.bMaxPacketSize0 = 0x09;
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
|
@ -824,7 +870,7 @@ static bool usbInitializeDeviceInterface5x(void)
|
||||||
.bDescriptorType = USB_DT_ENDPOINT,
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||||
.wMaxPacketSize = 0x40,
|
.wMaxPacketSize = USB_FS_EP_MAX_PACKET_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
||||||
|
@ -832,7 +878,7 @@ static bool usbInitializeDeviceInterface5x(void)
|
||||||
.bDescriptorType = USB_DT_ENDPOINT,
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||||
.wMaxPacketSize = 0x40,
|
.wMaxPacketSize = USB_FS_EP_MAX_PACKET_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
|
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
|
||||||
|
@ -881,8 +927,8 @@ static bool usbInitializeDeviceInterface5x(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* High Speed config (USB 2.0). */
|
/* High Speed config (USB 2.0). */
|
||||||
endpoint_descriptor_in.wMaxPacketSize = 0x200;
|
endpoint_descriptor_in.wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE;
|
||||||
endpoint_descriptor_out.wMaxPacketSize = 0x200;
|
endpoint_descriptor_out.wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE;
|
||||||
|
|
||||||
rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
|
@ -906,8 +952,8 @@ static bool usbInitializeDeviceInterface5x(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Super Speed config (USB 3.0). */
|
/* Super Speed config (USB 3.0). */
|
||||||
endpoint_descriptor_in.wMaxPacketSize = 0x400;
|
endpoint_descriptor_in.wMaxPacketSize = USB_SS_EP_MAX_PACKET_SIZE;
|
||||||
endpoint_descriptor_out.wMaxPacketSize = 0x400;
|
endpoint_descriptor_out.wMaxPacketSize = USB_SS_EP_MAX_PACKET_SIZE;
|
||||||
|
|
||||||
rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
|
@ -987,7 +1033,7 @@ static bool usbInitializeDeviceInterface1x(void)
|
||||||
.bDescriptorType = USB_DT_ENDPOINT,
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||||
.wMaxPacketSize = 0x200,
|
.wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
||||||
|
@ -995,7 +1041,7 @@ static bool usbInitializeDeviceInterface1x(void)
|
||||||
.bDescriptorType = USB_DT_ENDPOINT,
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||||
.wMaxPacketSize = 0x200,
|
.wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Enable device interface. */
|
/* Enable device interface. */
|
||||||
|
|
Loading…
Reference in a new issue