aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/sd/usb_flashdrive/lib-uhs2
diff options
context:
space:
mode:
Diffstat (limited to 'Marlin/src/sd/usb_flashdrive/lib-uhs2')
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.cpp795
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.h53
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/UsbCore.h312
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/address.h271
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/confdescparser.h201
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/hexdump.h68
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/macros.h86
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.cpp1217
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.h562
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/max3421e.h242
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/message.cpp128
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/message.h85
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.cpp77
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.h145
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/printhex.h80
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/settings.h236
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/usb_ch9.h170
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp207
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.h58
19 files changed, 4993 insertions, 0 deletions
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.cpp b/Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.cpp
new file mode 100644
index 0000000..f26e82b
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.cpp
@@ -0,0 +1,795 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+//
+// USB functions supporting Flash Drive
+//
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT) && DISABLED(USE_UHS3_USB)
+
+#include "Usb.h"
+
+static uint8_t usb_error = 0;
+static uint8_t usb_task_state;
+
+/* constructor */
+USB::USB() : bmHubPre(0) {
+ usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; // Set up state machine
+ init();
+}
+
+/* Initialize data structures */
+void USB::init() {
+ //devConfigIndex = 0;
+ bmHubPre = 0;
+}
+
+uint8_t USB::getUsbTaskState() { return usb_task_state; }
+void USB::setUsbTaskState(uint8_t state) { usb_task_state = state; }
+
+EpInfo* USB::getEpInfoEntry(uint8_t addr, uint8_t ep) {
+ UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
+
+ if (!p || !p->epinfo)
+ return nullptr;
+
+ EpInfo *pep = p->epinfo;
+
+ for (uint8_t i = 0; i < p->epcount; i++) {
+ if ((pep)->epAddr == ep)
+ return pep;
+
+ pep++;
+ }
+ return nullptr;
+}
+
+/**
+ * Set device table entry
+ * Each device is different and has different number of endpoints.
+ * This function plugs endpoint record structure, defined in application, to devtable
+ */
+uint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr) {
+ if (!eprecord_ptr)
+ return USB_ERROR_INVALID_ARGUMENT;
+
+ UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
+
+ if (!p)
+ return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+ p->address.devAddress = addr;
+ p->epinfo = eprecord_ptr;
+ p->epcount = epcount;
+
+ return 0;
+}
+
+uint8_t USB::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit) {
+ UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
+
+ if (!p)
+ return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+ if (!p->epinfo)
+ return USB_ERROR_EPINFO_IS_NULL;
+
+ *ppep = getEpInfoEntry(addr, ep);
+
+ if (!*ppep)
+ return USB_ERROR_EP_NOT_FOUND_IN_TBL;
+
+ *nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower));
+ (*nak_limit)--;
+ /*
+ USBTRACE2("\r\nAddress: ", addr);
+ USBTRACE2(" EP: ", ep);
+ USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower);
+ USBTRACE2(" NAK Limit: ", nak_limit);
+ USBTRACE("\r\n");
+ */
+ regWr(rPERADDR, addr); // Set peripheral address
+
+ uint8_t mode = regRd(rMODE);
+
+ //Serial.print("\r\nMode: ");
+ //Serial.println( mode, HEX);
+ //Serial.print("\r\nLS: ");
+ //Serial.println(p->lowspeed, HEX);
+
+ // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise
+ regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED));
+
+ return 0;
+}
+
+/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */
+/* depending on request. Actual requests are defined as inlines */
+/* return codes: */
+/* 00 = success */
+/* 01-0f = non-zero HRSLT */
+uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
+ uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p) {
+ bool direction = false; // Request direction, IN or OUT
+ uint8_t rcode;
+ SETUP_PKT setup_pkt;
+
+ EpInfo *pep = nullptr;
+ uint16_t nak_limit = 0;
+
+ rcode = SetAddress(addr, ep, &pep, &nak_limit);
+ if (rcode) return rcode;
+
+ direction = ((bmReqType & 0x80) > 0);
+
+ /* fill in setup packet */
+ setup_pkt.ReqType_u.bmRequestType = bmReqType;
+ setup_pkt.bRequest = bRequest;
+ setup_pkt.wVal_u.wValueLo = wValLo;
+ setup_pkt.wVal_u.wValueHi = wValHi;
+ setup_pkt.wIndex = wInd;
+ setup_pkt.wLength = total;
+
+ bytesWr(rSUDFIFO, 8, (uint8_t*) & setup_pkt); // Transfer to setup packet FIFO
+
+ rcode = dispatchPkt(tokSETUP, ep, nak_limit); // Dispatch packet
+ if (rcode) return rcode; // Return HRSLT if not zero
+
+ if (dataptr) { // Data stage, if present
+ if (direction) { // IN transfer
+ uint16_t left = total;
+ pep->bmRcvToggle = 1; // BmRCVTOG1;
+
+ while (left) {
+ // Bytes read into buffer
+ uint16_t read = nbytes;
+ //uint16_t read = (left<nbytes) ? left : nbytes;
+
+ rcode = InTransfer(pep, nak_limit, &read, dataptr);
+ if (rcode == hrTOGERR) {
+ // Yes, we flip it wrong here so that next time it is actually correct!
+ pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
+ continue;
+ }
+
+ if (rcode) return rcode;
+
+ // Invoke callback function if inTransfer completed successfully and callback function pointer is specified
+ if (!rcode && p) ((USBReadParser*)p)->Parse(read, dataptr, total - left);
+
+ left -= read;
+
+ if (read < nbytes) break;
+ }
+ }
+ else { // OUT transfer
+ pep->bmSndToggle = 1; // BmSNDTOG1;
+ rcode = OutTransfer(pep, nak_limit, nbytes, dataptr);
+ }
+ if (rcode) return rcode; // Return error
+ }
+ // Status stage
+ return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); // GET if direction
+}
+
+/**
+ * IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes.
+ * Keep sending INs and writes data to memory area pointed by 'data'
+ * rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error, fe = USB xfer timeout
+ */
+uint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data, uint8_t bInterval /*= 0*/) {
+ EpInfo *pep = nullptr;
+ uint16_t nak_limit = 0;
+
+ uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit);
+ if (rcode) {
+ USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81);
+ USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81);
+ USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81);
+ return rcode;
+ }
+ return InTransfer(pep, nak_limit, nbytesptr, data, bInterval);
+}
+
+uint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data, uint8_t bInterval /*= 0*/) {
+ uint8_t rcode = 0;
+ uint8_t pktsize;
+
+ uint16_t nbytes = *nbytesptr;
+ //printf("Requesting %i bytes ", nbytes);
+ uint8_t maxpktsize = pep->maxPktSize;
+
+ *nbytesptr = 0;
+ regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); // Set toggle value
+
+ // Use a 'break' to exit this loop
+ for (;;) {
+ rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); // IN packet to EP-'endpoint'. Function takes care of NAKS.
+ if (rcode == hrTOGERR) {
+ // Yes, we flip it wrong here so that next time it is actually correct!
+ pep->bmRcvToggle = (regRd(rHRSL) & bmRCVTOGRD) ? 0 : 1;
+ regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); // Set toggle value
+ continue;
+ }
+ if (rcode) {
+ //printf(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode);
+ break; // Should be 0, indicating ACK. Else return error code.
+ }
+ /* check for RCVDAVIRQ and generate error if not present */
+ /* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */
+ if ((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) {
+ //printf(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n");
+ rcode = 0xF0; // Receive error
+ break;
+ }
+ pktsize = regRd(rRCVBC); // Number of received bytes
+ //printf("Got %i bytes \r\n", pktsize);
+ // This would be OK, but...
+ //assert(pktsize <= nbytes);
+ if (pktsize > nbytes) {
+ // This can happen. Use of assert on Arduino locks up the Arduino.
+ // So I will trim the value, and hope for the best.
+ //printf(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize);
+ pktsize = nbytes;
+ }
+
+ int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr);
+ if (mem_left < 0) mem_left = 0;
+
+ data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data);
+
+ regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer
+ *nbytesptr += pktsize; // Add this packet's byte count to total transfer length
+
+ /* The transfer is complete under two conditions: */
+ /* 1. The device sent a short packet (L.T. maxPacketSize) */
+ /* 2. 'nbytes' have been transferred. */
+ if (pktsize < maxpktsize || *nbytesptr >= nbytes) { // Transferred 'nbytes' bytes?
+ // Save toggle value
+ pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0;
+ //printf("\r\n");
+ rcode = 0;
+ break;
+ }
+ else if (bInterval > 0)
+ delay(bInterval); // Delay according to polling interval
+ }
+ return rcode;
+}
+
+/**
+ * OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes.
+ * Handles NAK bug per Maxim Application Note 4000 for single buffer transfer
+ * rcode 0 if no errors. rcode 01-0f is relayed from HRSL
+ */
+uint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) {
+ EpInfo *pep = nullptr;
+ uint16_t nak_limit = 0;
+
+ uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit);
+ if (rcode) return rcode;
+
+ return OutTransfer(pep, nak_limit, nbytes, data);
+}
+
+uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) {
+ uint8_t rcode = hrSUCCESS, retry_count;
+ uint8_t *data_p = data; // Local copy of the data pointer
+ uint16_t bytes_tosend, nak_count;
+ uint16_t bytes_left = nbytes;
+
+ uint8_t maxpktsize = pep->maxPktSize;
+
+ if (maxpktsize < 1 || maxpktsize > 64)
+ return USB_ERROR_INVALID_MAX_PKT_SIZE;
+
+ uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT;
+
+ regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); // Set toggle value
+
+ while (bytes_left) {
+ retry_count = 0;
+ nak_count = 0;
+ bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left;
+ bytesWr(rSNDFIFO, bytes_tosend, data_p); // Filling output FIFO
+ regWr(rSNDBC, bytes_tosend); // Set number of bytes
+ regWr(rHXFR, (tokOUT | pep->epAddr)); // Dispatch packet
+ while (!(regRd(rHIRQ) & bmHXFRDNIRQ)); // Wait for the completion IRQ
+ regWr(rHIRQ, bmHXFRDNIRQ); // Clear IRQ
+ rcode = (regRd(rHRSL) & 0x0F);
+
+ while (rcode && ((int32_t)((uint32_t)millis() - timeout) < 0L)) {
+ switch (rcode) {
+ case hrNAK:
+ nak_count++;
+ if (nak_limit && (nak_count == nak_limit))
+ goto breakout;
+ //return rcode;
+ break;
+ case hrTIMEOUT:
+ retry_count++;
+ if (retry_count == USB_RETRY_LIMIT)
+ goto breakout;
+ //return rcode;
+ break;
+ case hrTOGERR:
+ // Yes, we flip it wrong here so that next time it is actually correct!
+ pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
+ regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); // Set toggle value
+ break;
+ default:
+ goto breakout;
+ }
+
+ /* process NAK according to Host out NAK bug */
+ regWr(rSNDBC, 0);
+ regWr(rSNDFIFO, *data_p);
+ regWr(rSNDBC, bytes_tosend);
+ regWr(rHXFR, (tokOUT | pep->epAddr)); // Dispatch packet
+ while (!(regRd(rHIRQ) & bmHXFRDNIRQ)); // Wait for the completion IRQ
+ regWr(rHIRQ, bmHXFRDNIRQ); // Clear IRQ
+ rcode = (regRd(rHRSL) & 0x0F);
+ } // While rcode && ....
+ bytes_left -= bytes_tosend;
+ data_p += bytes_tosend;
+ } // While bytes_left...
+breakout:
+
+ pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; // BmSNDTOG1 : bmSNDTOG0; // Update toggle
+ return ( rcode); // Should be 0 in all cases
+}
+
+/**
+ * Dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty
+ * If NAK, tries to re-send up to nak_limit times
+ * If nak_limit == 0, do not count NAKs, exit after timeout
+ * If bus timeout, re-sends up to USB_RETRY_LIMIT times
+ * return codes 0x00-0x0F are HRSLT( 0x00 being success ), 0xFF means timeout
+ */
+uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) {
+ uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT;
+ uint8_t tmpdata;
+ uint8_t rcode = hrSUCCESS;
+ uint8_t retry_count = 0;
+ uint16_t nak_count = 0;
+
+ while ((int32_t)((uint32_t)millis() - timeout) < 0L) {
+ #if defined(ESP8266) || defined(ESP32)
+ yield(); // Needed in order to reset the watchdog timer on the ESP8266
+ #endif
+ regWr(rHXFR, (token | ep)); // Launch the transfer
+ rcode = USB_ERROR_TRANSFER_TIMEOUT;
+
+ while ((int32_t)((uint32_t)millis() - timeout) < 0L) { // Wait for transfer completion
+ #if defined(ESP8266) || defined(ESP32)
+ yield(); // Needed to reset the watchdog timer on the ESP8266
+ #endif
+ tmpdata = regRd(rHIRQ);
+
+ if (tmpdata & bmHXFRDNIRQ) {
+ regWr(rHIRQ, bmHXFRDNIRQ); // Clear the interrupt
+ rcode = 0x00;
+ break;
+ }
+
+ } // While millis() < timeout
+
+ //if (rcode != 0x00) return rcode; // Exit if timeout
+
+ rcode = (regRd(rHRSL) & 0x0F); // Analyze transfer result
+
+ switch (rcode) {
+ case hrNAK:
+ nak_count++;
+ if (nak_limit && (nak_count == nak_limit))
+ return (rcode);
+ break;
+ case hrTIMEOUT:
+ retry_count++;
+ if (retry_count == USB_RETRY_LIMIT)
+ return (rcode);
+ break;
+ default:
+ return (rcode);
+ }
+
+ } // While timeout > millis()
+ return rcode;
+}
+
+// USB main task. Performs enumeration/cleanup
+void USB::Task() { // USB state machine
+ uint8_t rcode;
+ uint8_t tmpdata;
+ static uint32_t delay = 0;
+ //USB_FD_DEVICE_DESCRIPTOR buf;
+ bool lowspeed = false;
+
+ MAX3421E::Task();
+
+ tmpdata = getVbusState();
+
+ /* modify USB task state if Vbus changed */
+ switch (tmpdata) {
+ case SE1: // Illegal state
+ usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL;
+ lowspeed = false;
+ break;
+ case SE0: // Disconnected
+ if ((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED)
+ usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
+ lowspeed = false;
+ break;
+ case LSHOST:
+ lowspeed = true;
+ // Intentional fallthrough
+ case FSHOST: // Attached
+ if ((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) {
+ delay = (uint32_t)millis() + USB_SETTLE_DELAY;
+ usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;
+ }
+ break;
+ }
+
+ for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
+ if (devConfig[i]) rcode = devConfig[i]->Poll();
+
+ switch (usb_task_state) {
+ case USB_DETACHED_SUBSTATE_INITIALIZE:
+ init();
+
+ for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
+ if (devConfig[i])
+ rcode = devConfig[i]->Release();
+
+ usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE;
+ break;
+ case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: // Just sit here
+ break;
+ case USB_DETACHED_SUBSTATE_ILLEGAL: // Just sit here
+ break;
+ case USB_ATTACHED_SUBSTATE_SETTLE: // Settle time for just attached device
+ if ((int32_t)((uint32_t)millis() - delay) >= 0L)
+ usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE;
+ else break; // Don't fall through
+ case USB_ATTACHED_SUBSTATE_RESET_DEVICE:
+ regWr(rHCTL, bmBUSRST); // Issue bus reset
+ usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE;
+ break;
+ case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE:
+ if ((regRd(rHCTL) & bmBUSRST) == 0) {
+ tmpdata = regRd(rMODE) | bmSOFKAENAB; // Start SOF generation
+ regWr(rMODE, tmpdata);
+ usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF;
+ //delay = (uint32_t)millis() + 20; // 20ms wait after reset per USB spec
+ }
+ break;
+ case USB_ATTACHED_SUBSTATE_WAIT_SOF: // Todo: change check order
+ if (regRd(rHIRQ) & bmFRAMEIRQ) {
+ // When first SOF received _and_ 20ms has passed we can continue
+ /*
+ if (delay < (uint32_t)millis()) // 20ms passed
+ usb_task_state = USB_STATE_CONFIGURING;
+ */
+ usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET;
+ delay = (uint32_t)millis() + 20;
+ }
+ break;
+ case USB_ATTACHED_SUBSTATE_WAIT_RESET:
+ if ((int32_t)((uint32_t)millis() - delay) >= 0L) usb_task_state = USB_STATE_CONFIGURING;
+ else break; // Don't fall through
+ case USB_STATE_CONFIGURING:
+
+ //Serial.print("\r\nConf.LS: ");
+ //Serial.println(lowspeed, HEX);
+
+ rcode = Configuring(0, 0, lowspeed);
+
+ if (!rcode)
+ usb_task_state = USB_STATE_RUNNING;
+ else if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) {
+ usb_error = rcode;
+ usb_task_state = USB_STATE_ERROR;
+ }
+ break;
+ case USB_STATE_RUNNING:
+ break;
+ case USB_STATE_ERROR:
+ //MAX3421E::Init();
+ break;
+ }
+}
+
+uint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) {
+ //uint8_t buf[12];
+ uint8_t rcode;
+ UsbDevice *p0 = nullptr, *p = nullptr;
+
+ // Get pointer to pseudo device with address 0 assigned
+ p0 = addrPool.GetUsbDevicePtr(0);
+ if (!p0) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+ if (!p0->epinfo) return USB_ERROR_EPINFO_IS_NULL;
+
+ p0->lowspeed = lowspeed;
+
+ // Allocate new address according to device class
+ uint8_t bAddress = addrPool.AllocAddress(parent, false, port);
+ if (!bAddress) return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+ p = addrPool.GetUsbDevicePtr(bAddress);
+ if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+ p->lowspeed = lowspeed;
+
+ // Assign new address to the device
+ rcode = setAddr(0, 0, bAddress);
+ if (rcode) {
+ addrPool.FreeAddress(bAddress);
+ bAddress = 0;
+ }
+ return rcode;
+}
+
+uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) {
+ //printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port);
+ uint8_t retries = 0;
+
+again:
+ uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed);
+ if (rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) {
+ if (parent == 0) {
+ // Send a bus reset on the root interface.
+ regWr(rHCTL, bmBUSRST); // Issue bus reset
+ delay(102); // Delay 102ms, compensate for clock inaccuracy.
+ }
+ else {
+ // Reset parent port
+ devConfig[parent]->ResetHubPort(port);
+ }
+ }
+ else if (rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works
+ delay(100);
+ retries++;
+ goto again;
+ }
+ else if (rcode)
+ return rcode;
+
+ rcode = devConfig[driver]->Init(parent, port, lowspeed);
+ if (rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works
+ delay(100);
+ retries++;
+ goto again;
+ }
+
+ if (rcode) {
+ // Issue a bus reset, because the device may be in a limbo state
+ if (parent == 0) {
+ // Send a bus reset on the root interface.
+ regWr(rHCTL, bmBUSRST); // Issue bus reset
+ delay(102); // Delay 102ms, compensate for clock inaccuracy.
+ }
+ else {
+ // Reset parent port
+ devConfig[parent]->ResetHubPort(port);
+ }
+ }
+ return rcode;
+}
+
+/**
+ * This is broken. It needs to enumerate differently.
+ * It causes major problems with several devices if detected in an unexpected order.
+ *
+ * Oleg - I wouldn't do anything before the newly connected device is considered sane.
+ * i.e.(delays are not indicated for brevity):
+ * 1. reset
+ * 2. GetDevDescr();
+ * 3a. If ACK, continue with allocating address, addressing, etc.
+ * 3b. Else reset again, count resets, stop at some number (5?).
+ * 4. When max.number of resets is reached, toggle power/fail
+ * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD()
+ * it doesn't need to be reset again
+ * New steps proposal:
+ * 1: get address pool instance. exit on fail
+ * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail.
+ * 3: bus reset, 100ms delay
+ * 4: set address
+ * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail
+ * 6: while (configurations) {
+ * for (each configuration) {
+ * for (each driver) {
+ * 6a: Ask device if it likes configuration. Returns 0 on OK.
+ * If successful, the driver configured device.
+ * The driver now owns the endpoints, and takes over managing them.
+ * The following will need codes:
+ * Everything went well, instance consumed, exit with success.
+ * Instance already in use, ignore it, try next driver.
+ * Not a supported device, ignore it, try next driver.
+ * Not a supported configuration for this device, ignore it, try next driver.
+ * Could not configure device, fatal, exit with fail.
+ * }
+ * }
+ * }
+ * 7: for (each driver) {
+ * 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID
+ * 8: if we get here, no driver likes the device plugged in, so exit failure.
+ */
+uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) {
+ //uint8_t bAddress = 0;
+ //printf("Configuring: parent = %i, port = %i\r\n", parent, port);
+ uint8_t devConfigIndex;
+ uint8_t rcode = 0;
+ uint8_t buf[sizeof (USB_FD_DEVICE_DESCRIPTOR)];
+ USB_FD_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_FD_DEVICE_DESCRIPTOR *>(buf);
+ UsbDevice *p = nullptr;
+ EpInfo *oldep_ptr = nullptr;
+ EpInfo epInfo;
+
+ epInfo.epAddr = 0;
+ epInfo.maxPktSize = 8;
+ epInfo.bmSndToggle = 0;
+ epInfo.bmRcvToggle = 0;
+ epInfo.bmNakPower = USB_NAK_MAX_POWER;
+
+ //delay(2000);
+ AddressPool &addrPool = GetAddressPool();
+ // Get pointer to pseudo device with address 0 assigned
+ p = addrPool.GetUsbDevicePtr(0);
+ if (!p) {
+ //printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n");
+ return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+ }
+
+ // Save old pointer to EP_RECORD of address 0
+ oldep_ptr = p->epinfo;
+
+ // Temporary assign new pointer to epInfo to p->epinfo in order to
+ // Avoid toggle inconsistence
+
+ p->epinfo = &epInfo;
+
+ p->lowspeed = lowspeed;
+ // Get device descriptor
+ rcode = getDevDescr(0, 0, sizeof (USB_FD_DEVICE_DESCRIPTOR), (uint8_t*)buf);
+
+ // Restore p->epinfo
+ p->epinfo = oldep_ptr;
+
+ if (rcode) {
+ //printf("Configuring error: Can't get USB_FD_DEVICE_DESCRIPTOR\r\n");
+ return rcode;
+ }
+
+ // To-do?
+ // Allocate new address according to device class
+ //bAddress = addrPool.AllocAddress(parent, false, port);
+
+ uint16_t vid = udd->idVendor, pid = udd->idProduct;
+ uint8_t klass = udd->bDeviceClass, subklass = udd->bDeviceSubClass;
+
+ // Attempt to configure if VID/PID or device class matches with a driver
+ // Qualify with subclass too.
+ //
+ // VID/PID & class tests default to false for drivers not yet ported
+ // Subclass defaults to true, so you don't have to define it if you don't have to.
+ //
+ for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {
+ if (!devConfig[devConfigIndex]) continue; // No driver
+ if (devConfig[devConfigIndex]->GetAddress()) continue; // Consumed
+ if (devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) {
+ rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);
+ if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED)
+ break;
+ }
+ }
+
+ if (devConfigIndex < USB_NUMDEVICES) return rcode;
+
+ // Blindly attempt to configure
+ for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {
+ if (!devConfig[devConfigIndex]) continue;
+ if (devConfig[devConfigIndex]->GetAddress()) continue; // Consumed
+ if (devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above
+ rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);
+
+ //printf("ERROR ENUMERATING %2.2x\r\n", rcode);
+ if (!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) {
+ // In case of an error dev_index should be reset to 0
+ // in order to start from the very beginning the
+ // next time the program gets here
+ //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)
+ //devConfigIndex = 0;
+ return rcode;
+ }
+ }
+ // Arriving here means the device class is unsupported by registered classes
+ return DefaultAddressing(parent, port, lowspeed);
+}
+
+uint8_t USB::ReleaseDevice(uint8_t addr) {
+ if (addr) {
+ for (uint8_t i = 0; i < USB_NUMDEVICES; i++) {
+ if (!devConfig[i]) continue;
+ if (devConfig[i]->GetAddress() == addr)
+ return devConfig[i]->Release();
+ }
+ }
+ return 0;
+}
+
+// Get device descriptor
+uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
+ return ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, nullptr);
+}
+
+// Get configuration descriptor
+uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
+ return ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, nullptr);
+}
+
+/**
+ * Requests Configuration Descriptor. Sends two Get Conf Descr requests.
+ * The first one gets the total length of all descriptors, then the second one requests this
+ * total length. The length of the first request can be shorter (4 bytes), however, there are
+ * devices which won't work unless this length is set to 9.
+ */
+uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p) {
+ const uint8_t bufSize = 64;
+ uint8_t buf[bufSize];
+ USB_FD_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_FD_CONFIGURATION_DESCRIPTOR *>(buf);
+
+ uint8_t ret = getConfDescr(addr, ep, 9, conf, buf);
+ if (ret) return ret;
+
+ uint16_t total = ucd->wTotalLength;
+
+ //USBTRACE2("\r\ntotal conf.size:", total);
+
+ return ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p);
+}
+
+// Get string descriptor
+uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) {
+ return ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, nullptr);
+}
+
+// Set address
+uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
+ uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, nullptr, nullptr);
+ //delay(2); // Per USB 2.0 sect.9.2.6.3
+ delay(300); // Older spec says you should wait at least 200ms
+ return rcode;
+ //return ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, nullptr, nullptr);
+}
+
+// Set configuration
+uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
+ return ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, nullptr, nullptr);
+}
+
+#endif // USB_FLASH_DRIVE_SUPPORT
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.h
new file mode 100644
index 0000000..b1ec146
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/Usb.h
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+/* USB functions */
+#define _usb_h_
+
+#include "../../../inc/MarlinConfigPre.h"
+
+// WARNING: Do not change the order of includes, or stuff will break!
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+
+// None of these should ever be included by a driver, or a user's sketch.
+#include "settings.h"
+#include "printhex.h"
+#include "message.h"
+
+#include "hexdump.h"
+//#include "sink_parser.h"
+#include "max3421e.h"
+#include "address.h"
+//#include "avrpins.h"
+#include "usb_ch9.h"
+#include "usbhost.h"
+#include "UsbCore.h"
+#include "parsetools.h"
+#include "confdescparser.h"
+
+#undef _usb_h_
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/UsbCore.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/UsbCore.h
new file mode 100644
index 0000000..d94958d
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/UsbCore.h
@@ -0,0 +1,312 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#ifndef _usb_h_
+ #error "Never include UsbCore.h directly; include Usb.h instead"
+#endif
+
+#pragma once
+
+// Not used anymore? If anyone uses this, please let us know so that this may be
+// moved to the proper place, settings.h.
+//#define USB_METHODS_INLINE
+
+/* shield pins. First parameter - SS pin, second parameter - INT pin */
+
+#ifdef __MARLIN_FIRMWARE__
+typedef MAX3421e MAX3421E; // Marlin redefines this class in "../usb_host.h"
+#elif defined(BOARD_BLACK_WIDDOW)
+typedef MAX3421e<P6, P3> MAX3421E; // Black Widow
+#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
+#if EXT_RAM
+typedef MAX3421e<P20, P7> MAX3421E; // Teensy++ 2.0 with XMEM2
+#else
+typedef MAX3421e<P9, P8> MAX3421E; // Teensy++ 1.0 and 2.0
+#endif
+#elif defined(BOARD_MEGA_ADK)
+typedef MAX3421e<P53, P54> MAX3421E; // Arduino Mega ADK
+#elif defined(ARDUINO_AVR_BALANDUINO)
+typedef MAX3421e<P20, P19> MAX3421E; // Balanduino
+#elif defined(__ARDUINO_X86__) && PLATFORM_ID == 0x06
+typedef MAX3421e<P3, P2> MAX3421E; // The Intel Galileo supports much faster read and write speed at pin 2 and 3
+#elif defined(ESP8266)
+typedef MAX3421e<P15, P5> MAX3421E; // ESP8266 boards
+#elif defined(ESP32)
+typedef MAX3421e<P5, P17> MAX3421E; // ESP32 boards
+#else
+typedef MAX3421e<P10, P9> MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.), Intel Edison, Intel Galileo 2 or Teensy 2.0 and 3.x
+#endif
+
+/* Common setup data constant combinations */
+#define bmREQ_GET_DESCR USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //get descriptor request type
+#define bmREQ_SET USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //set request type for all but 'set feature' and 'set interface'
+#define bmREQ_CL_GET_INTF USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE //get interface request type
+
+// D7 data transfer direction (0 - host-to-device, 1 - device-to-host)
+// D6-5 Type (0- standard, 1 - class, 2 - vendor, 3 - reserved)
+// D4-0 Recipient (0 - device, 1 - interface, 2 - endpoint, 3 - other, 4..31 - reserved)
+
+// USB Device Classes
+#define USB_CLASS_USE_CLASS_INFO 0x00 // Use Class Info in the Interface Descriptors
+#define USB_CLASS_AUDIO 0x01 // Audio
+#define USB_CLASS_COM_AND_CDC_CTRL 0x02 // Communications and CDC Control
+#define USB_CLASS_HID 0x03 // HID
+#define USB_CLASS_PHYSICAL 0x05 // Physical
+#define USB_CLASS_IMAGE 0x06 // Image
+#define USB_CLASS_PRINTER 0x07 // Printer
+#define USB_CLASS_MASS_STORAGE 0x08 // Mass Storage
+#define USB_CLASS_HUB 0x09 // Hub
+#define USB_CLASS_CDC_DATA 0x0A // CDC-Data
+#define USB_CLASS_SMART_CARD 0x0B // Smart-Card
+#define USB_CLASS_CONTENT_SECURITY 0x0D // Content Security
+#define USB_CLASS_VIDEO 0x0E // Video
+#define USB_CLASS_PERSONAL_HEALTH 0x0F // Personal Healthcare
+#define USB_CLASS_DIAGNOSTIC_DEVICE 0xDC // Diagnostic Device
+#define USB_CLASS_WIRELESS_CTRL 0xE0 // Wireless Controller
+#define USB_CLASS_MISC 0xEF // Miscellaneous
+#define USB_CLASS_APP_SPECIFIC 0xFE // Application Specific
+#define USB_CLASS_VENDOR_SPECIFIC 0xFF // Vendor Specific
+
+// Additional Error Codes
+#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED 0xD1
+#define USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE 0xD2
+#define USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS 0xD3
+#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL 0xD4
+#define USB_ERROR_HUB_ADDRESS_OVERFLOW 0xD5
+#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL 0xD6
+#define USB_ERROR_EPINFO_IS_NULL 0xD7
+#define USB_ERROR_INVALID_ARGUMENT 0xD8
+#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE 0xD9
+#define USB_ERROR_INVALID_MAX_PKT_SIZE 0xDA
+#define USB_ERROR_EP_NOT_FOUND_IN_TBL 0xDB
+#define USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET 0xE0
+#define USB_ERROR_FailGetDevDescr 0xE1
+#define USB_ERROR_FailSetDevTblEntry 0xE2
+#define USB_ERROR_FailGetConfDescr 0xE3
+#define USB_ERROR_TRANSFER_TIMEOUT 0xFF
+
+#define USB_XFER_TIMEOUT 5000 // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec
+//#define USB_NAK_LIMIT 32000 // NAK limit for a transfer. 0 means NAKs are not counted
+#define USB_RETRY_LIMIT 3 // 3 retry limit for a transfer
+#define USB_SETTLE_DELAY 200 // settle delay in milliseconds
+
+#define USB_NUMDEVICES 16 //number of USB devices
+//#define HUB_MAX_HUBS 7 // maximum number of hubs that can be attached to the host controller
+#define HUB_PORT_RESET_DELAY 20 // hub port reset delay 10 ms recomended, can be up to 20 ms
+
+/* USB state machine states */
+#define USB_STATE_MASK 0xF0
+
+#define USB_STATE_DETACHED 0x10
+#define USB_DETACHED_SUBSTATE_INITIALIZE 0x11
+#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE 0x12
+#define USB_DETACHED_SUBSTATE_ILLEGAL 0x13
+#define USB_ATTACHED_SUBSTATE_SETTLE 0x20
+#define USB_ATTACHED_SUBSTATE_RESET_DEVICE 0x30
+#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE 0x40
+#define USB_ATTACHED_SUBSTATE_WAIT_SOF 0x50
+#define USB_ATTACHED_SUBSTATE_WAIT_RESET 0x51
+#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE 0x60
+#define USB_STATE_ADDRESSING 0x70
+#define USB_STATE_CONFIGURING 0x80
+#define USB_STATE_RUNNING 0x90
+#define USB_STATE_ERROR 0xA0
+
+class USBDeviceConfig {
+public:
+
+ virtual uint8_t Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed __attribute__((unused))) {
+ return 0;
+ }
+
+ virtual uint8_t ConfigureDevice(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed __attribute__((unused))) {
+ return 0;
+ }
+
+ virtual uint8_t Release() {
+ return 0;
+ }
+
+ virtual uint8_t Poll() {
+ return 0;
+ }
+
+ virtual uint8_t GetAddress() {
+ return 0;
+ }
+
+ virtual void ResetHubPort(uint8_t port __attribute__((unused))) {
+ return;
+ } // Note used for hubs only!
+
+ virtual bool VIDPIDOK(uint16_t vid __attribute__((unused)), uint16_t pid __attribute__((unused))) {
+ return false;
+ }
+
+ virtual bool DEVCLASSOK(uint8_t klass __attribute__((unused))) {
+ return false;
+ }
+
+ virtual bool DEVSUBCLASSOK(uint8_t subklass __attribute__((unused))) {
+ return true;
+ }
+
+};
+
+/* USB Setup Packet Structure */
+typedef struct {
+
+ union { // offset description
+ uint8_t bmRequestType; // 0 Bit-map of request type
+
+ struct {
+ uint8_t recipient : 5; // Recipient of the request
+ uint8_t type : 2; // Type of request
+ uint8_t direction : 1; // Direction of data X-fer
+ } __attribute__((packed));
+ } ReqType_u;
+ uint8_t bRequest; // 1 Request
+
+ union {
+ uint16_t wValue; // 2 Depends on bRequest
+
+ struct {
+ uint8_t wValueLo;
+ uint8_t wValueHi;
+ } __attribute__((packed));
+ } wVal_u;
+ uint16_t wIndex; // 4 Depends on bRequest
+ uint16_t wLength; // 6 Depends on bRequest
+} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT;
+
+
+
+// Base class for incoming data parser
+
+class USBReadParser {
+public:
+ virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) = 0;
+};
+
+class USB : public MAX3421E {
+ AddressPoolImpl<USB_NUMDEVICES> addrPool;
+ USBDeviceConfig* devConfig[USB_NUMDEVICES];
+ uint8_t bmHubPre;
+
+public:
+ USB();
+
+ void SetHubPreMask() {
+ bmHubPre |= bmHUBPRE;
+ };
+
+ void ResetHubPreMask() {
+ bmHubPre &= (~bmHUBPRE);
+ };
+
+ AddressPool& GetAddressPool() {
+ return (AddressPool&)addrPool;
+ };
+
+ uint8_t RegisterDeviceClass(USBDeviceConfig *pdev) {
+ for (uint8_t i = 0; i < USB_NUMDEVICES; i++) {
+ if (!devConfig[i]) {
+ devConfig[i] = pdev;
+ return 0;
+ }
+ }
+ return USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS;
+ };
+
+ void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
+ addrPool.ForEachUsbDevice(pfunc);
+ };
+ uint8_t getUsbTaskState();
+ void setUsbTaskState(uint8_t state);
+
+ EpInfo* getEpInfoEntry(uint8_t addr, uint8_t ep);
+ uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr);
+
+ /* Control requests */
+ uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr);
+ uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr);
+
+ uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p);
+
+ uint8_t getStrDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t* dataptr);
+ uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr);
+ uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value);
+ /**/
+ uint8_t ctrlData(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr, bool direction);
+ uint8_t ctrlStatus(uint8_t ep, bool direction, uint16_t nak_limit);
+ uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data, uint8_t bInterval = 0);
+ uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data);
+ uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);
+
+ void Task();
+
+ uint8_t DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed);
+ uint8_t Configuring(uint8_t parent, uint8_t port, bool lowspeed);
+ uint8_t ReleaseDevice(uint8_t addr);
+
+ uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
+ uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p);
+
+private:
+ void init();
+ uint8_t SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit);
+ uint8_t OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);
+ uint8_t InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval = 0);
+ uint8_t AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed);
+};
+
+#if 0 //defined(USB_METHODS_INLINE)
+//get device descriptor
+
+inline uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
+ return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr));
+}
+//get configuration descriptor
+
+inline uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
+ return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr));
+}
+//get string descriptor
+
+inline uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t nuint8_ts, uint8_t index, uint16_t langid, uint8_t* dataptr) {
+ return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nuint8_ts, dataptr));
+}
+//set address
+
+inline uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
+ return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, nullptr));
+}
+//set configuration
+
+inline uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
+ return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, nullptr));
+}
+
+#endif // defined(USB_METHODS_INLINE)
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/address.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/address.h
new file mode 100644
index 0000000..373b95a
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/address.h
@@ -0,0 +1,271 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include address.h directly; include Usb.h instead"
+#endif
+
+/* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */
+/* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */
+#define USB_NAK_MAX_POWER 15 //NAK binary order maximum value
+#define USB_NAK_DEFAULT 14 //default 32K-1 NAKs before giving up
+#define USB_NAK_NOWAIT 1 //Single NAK stops transfer
+#define USB_NAK_NONAK 0 //Do not count NAKs, stop retrying after USB Timeout
+
+struct EpInfo {
+ uint8_t epAddr; // Endpoint address
+ uint8_t maxPktSize; // Maximum packet size
+
+ union {
+ uint8_t epAttribs;
+
+ struct {
+ uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise
+ uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise
+ uint8_t bmNakPower : 6; // Binary order for NAK_LIMIT value
+ } __attribute__((packed));
+ };
+} __attribute__((packed));
+
+// 7 6 5 4 3 2 1 0
+// ---------------------------------
+// | | H | P | P | P | A | A | A |
+// ---------------------------------
+//
+// H - if 1 the address is a hub address
+// P - parent hub address
+// A - device address / port number in case of hub
+//
+
+struct UsbDeviceAddress {
+ union {
+ struct {
+ uint8_t bmAddress : 3; // device address/port number
+ uint8_t bmParent : 3; // parent hub address
+ uint8_t bmHub : 1; // hub flag
+ uint8_t bmReserved : 1; // reserved, must be zero
+ } __attribute__((packed));
+ uint8_t devAddress;
+ };
+} __attribute__((packed));
+
+#define bmUSB_DEV_ADDR_ADDRESS 0x07
+#define bmUSB_DEV_ADDR_PARENT 0x38
+#define bmUSB_DEV_ADDR_HUB 0x40
+
+struct UsbDevice {
+ EpInfo *epinfo; // endpoint info pointer
+ UsbDeviceAddress address;
+ uint8_t epcount; // number of endpoints
+ bool lowspeed; // indicates if a device is the low speed one
+ // uint8_t devclass; // device class
+} __attribute__((packed));
+
+class AddressPool {
+ public:
+ virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0;
+ virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0;
+ virtual void FreeAddress(uint8_t addr) = 0;
+};
+
+typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);
+
+#define ADDR_ERROR_INVALID_INDEX 0xFF
+#define ADDR_ERROR_INVALID_ADDRESS 0xFF
+
+template <const uint8_t MAX_DEVICES_ALLOWED>
+class AddressPoolImpl : public AddressPool {
+ EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device
+
+ uint8_t hubCounter; // hub counter is kept
+ // in order to avoid hub address duplication
+
+ UsbDevice thePool[MAX_DEVICES_ALLOWED];
+
+ // Initialize address pool entry
+
+ void InitEntry(uint8_t index) {
+ thePool[index].address.devAddress = 0;
+ thePool[index].epcount = 1;
+ thePool[index].lowspeed = 0;
+ thePool[index].epinfo = &dev0ep;
+ }
+
+ // Return thePool index for a given address
+
+ uint8_t FindAddressIndex(uint8_t address = 0) {
+ for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
+ if (thePool[i].address.devAddress == address)
+ return i;
+
+ return 0;
+ }
+
+ // Return thePool child index for a given parent
+
+ uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) {
+ for (uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) {
+ if (thePool[i].address.bmParent == addr.bmAddress)
+ return i;
+ }
+ return 0;
+ }
+
+ // Frees address entry specified by index parameter
+
+ void FreeAddressByIndex(uint8_t index) {
+ // Zero field is reserved and should not be affected
+ if (index == 0) return;
+
+ UsbDeviceAddress uda = thePool[index].address;
+ // If a hub was switched off all port addresses should be freed
+ if (uda.bmHub == 1) {
+ for (uint8_t i = 1; (i = FindChildIndex(uda, i));)
+ FreeAddressByIndex(i);
+
+ // If the hub had the last allocated address, hubCounter should be decremented
+ if (hubCounter == uda.bmAddress) hubCounter--;
+ }
+ InitEntry(index);
+ }
+
+ // Initialize the whole address pool at once
+
+ void InitAllAddresses() {
+ for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
+ InitEntry(i);
+
+ hubCounter = 0;
+ }
+
+public:
+
+ AddressPoolImpl() : hubCounter(0) {
+ // Zero address is reserved
+ InitEntry(0);
+
+ thePool[0].address.devAddress = 0;
+ thePool[0].epinfo = &dev0ep;
+ dev0ep.epAddr = 0;
+ dev0ep.maxPktSize = 8;
+ dev0ep.bmSndToggle = 0; // Set DATA0/1 toggles to 0
+ dev0ep.bmRcvToggle = 0;
+ dev0ep.bmNakPower = USB_NAK_MAX_POWER;
+
+ InitAllAddresses();
+ }
+
+ // Return a pointer to a specified address entry
+
+ virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) {
+ if (!addr) return thePool;
+ uint8_t index = FindAddressIndex(addr);
+ return index ? thePool + index : nullptr;
+ }
+
+ // Perform an operation specified by pfunc for each addressed device
+
+ void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
+ if (pfunc) {
+ for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
+ if (thePool[i].address.devAddress)
+ pfunc(thePool + i);
+ }
+ }
+
+ // Allocate new address
+
+ virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) {
+ /* if (parent != 0 && port == 0)
+ USB_HOST_SERIAL.println("PRT:0"); */
+ UsbDeviceAddress _parent;
+ _parent.devAddress = parent;
+ if (_parent.bmReserved || port > 7)
+ //if(parent > 127 || port > 7)
+ return 0;
+
+ if (is_hub && hubCounter == 7) return 0;
+
+ // finds first empty address entry starting from one
+ uint8_t index = FindAddressIndex(0);
+
+ if (!index) return 0; // if empty entry is not found
+
+ if (_parent.devAddress == 0) {
+ if (is_hub) {
+ thePool[index].address.devAddress = 0x41;
+ hubCounter++;
+ }
+ else
+ thePool[index].address.devAddress = 1;
+
+ return thePool[index].address.devAddress;
+ }
+
+ UsbDeviceAddress addr;
+ addr.devAddress = 0; // Ensure all bits are zero
+ addr.bmParent = _parent.bmAddress;
+ if (is_hub) {
+ addr.bmHub = 1;
+ addr.bmAddress = ++hubCounter;
+ }
+ else {
+ addr.bmHub = 0;
+ addr.bmAddress = port;
+ }
+ thePool[index].address = addr;
+ /*
+ USB_HOST_SERIAL.print("Addr:");
+ USB_HOST_SERIAL.print(addr.bmHub, HEX);
+ USB_HOST_SERIAL.print(".");
+ USB_HOST_SERIAL.print(addr.bmParent, HEX);
+ USB_HOST_SERIAL.print(".");
+ USB_HOST_SERIAL.println(addr.bmAddress, HEX);
+ */
+ return thePool[index].address.devAddress;
+ }
+
+ // Empty the pool entry
+
+ virtual void FreeAddress(uint8_t addr) {
+ // if the root hub is disconnected all the addresses should be initialized
+ if (addr == 0x41) {
+ InitAllAddresses();
+ return;
+ }
+ FreeAddressByIndex(FindAddressIndex(addr));
+ }
+
+ // Return number of hubs attached
+ // It can be helpful to find out if hubs are attached when getting the exact number of hubs.
+ //uint8_t GetNumHubs() { return hubCounter; }
+ //uint8_t GetNumDevices() {
+ // uint8_t counter = 0;
+ // for (uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
+ // if (thePool[i].address != 0); counter++;
+ // return counter;
+ //}
+};
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/confdescparser.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/confdescparser.h
new file mode 100644
index 0000000..9ed35ff
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/confdescparser.h
@@ -0,0 +1,201 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include confdescparser.h directly; include Usb.h instead"
+#endif
+
+class UsbConfigXtracter {
+public:
+ //virtual void ConfigXtract(const USB_FD_CONFIGURATION_DESCRIPTOR *conf) = 0;
+ //virtual void InterfaceXtract(uint8_t conf, const USB_FD_INTERFACE_DESCRIPTOR *iface) = 0;
+
+ virtual void EndpointXtract(uint8_t conf __attribute__((unused)), uint8_t iface __attribute__((unused)), uint8_t alt __attribute__((unused)), uint8_t proto __attribute__((unused)), const USB_FD_ENDPOINT_DESCRIPTOR *ep __attribute__((unused))) {
+ }
+};
+
+#define CP_MASK_COMPARE_CLASS 1
+#define CP_MASK_COMPARE_SUBCLASS 2
+#define CP_MASK_COMPARE_PROTOCOL 4
+#define CP_MASK_COMPARE_ALL 7
+
+// Configuration Descriptor Parser Class Template
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+class ConfigDescParser : public USBReadParser {
+ UsbConfigXtracter *theXtractor;
+ MultiValueBuffer theBuffer;
+ MultiByteValueParser valParser;
+ ByteSkipper theSkipper;
+ uint8_t varBuffer[16 /*sizeof(USB_FD_CONFIGURATION_DESCRIPTOR)*/];
+
+ uint8_t stateParseDescr; // ParseDescriptor state
+
+ uint8_t dscrLen; // Descriptor length
+ uint8_t dscrType; // Descriptor type
+
+ bool isGoodInterface; // Apropriate interface flag
+ uint8_t confValue; // Configuration value
+ uint8_t protoValue; // Protocol value
+ uint8_t ifaceNumber; // Interface number
+ uint8_t ifaceAltSet; // Interface alternate settings
+
+ bool UseOr;
+ bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn);
+ void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);
+
+public:
+
+ void SetOR() { UseOr = true; }
+ ConfigDescParser(UsbConfigXtracter *xtractor);
+ void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset);
+};
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(UsbConfigXtracter *xtractor) :
+ theXtractor(xtractor),
+ stateParseDescr(0),
+ dscrLen(0),
+ dscrType(0),
+ UseOr(false) {
+ theBuffer.pValue = varBuffer;
+ valParser.Initialize(&theBuffer);
+ theSkipper.Initialize(&theBuffer);
+ };
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset __attribute__((unused))) {
+ uint16_t cntdn = (uint16_t)len;
+ uint8_t *p = (uint8_t*)pbuf;
+ while (cntdn) if (!ParseDescriptor(&p, &cntdn)) return;
+}
+
+/* Parser for the configuration descriptor. Takes values for class, subclass, protocol fields in interface descriptor and
+ compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) {
+ USB_FD_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast<USB_FD_CONFIGURATION_DESCRIPTOR*>(varBuffer);
+ USB_FD_INTERFACE_DESCRIPTOR* uid = reinterpret_cast<USB_FD_INTERFACE_DESCRIPTOR*>(varBuffer);
+ switch (stateParseDescr) {
+ case 0:
+ theBuffer.valueSize = 2;
+ valParser.Initialize(&theBuffer);
+ stateParseDescr = 1;
+ case 1:
+ if (!valParser.Parse(pp, pcntdn)) return false;
+ dscrLen = *((uint8_t*)theBuffer.pValue);
+ dscrType = *((uint8_t*)theBuffer.pValue + 1);
+ stateParseDescr = 2;
+ case 2:
+ // This is a sort of hack. Assuming that two bytes are all ready in the buffer
+ // the pointer is positioned two bytes ahead in order for the rest of descriptor
+ // to be read right after the size and the type fields.
+ // This should be used carefully. varBuffer should be used directly to handle data
+ // in the buffer.
+ theBuffer.pValue = varBuffer + 2;
+ stateParseDescr = 3;
+ case 3:
+ switch (dscrType) {
+ case USB_DESCRIPTOR_INTERFACE:
+ isGoodInterface = false;
+ break;
+ case USB_DESCRIPTOR_CONFIGURATION:
+ case USB_DESCRIPTOR_ENDPOINT:
+ case HID_DESCRIPTOR_HID:
+ break;
+ }
+ theBuffer.valueSize = dscrLen - 2;
+ valParser.Initialize(&theBuffer);
+ stateParseDescr = 4;
+ case 4:
+ switch (dscrType) {
+ case USB_DESCRIPTOR_CONFIGURATION:
+ if (!valParser.Parse(pp, pcntdn)) return false;
+ confValue = ucd->bConfigurationValue;
+ break;
+ case USB_DESCRIPTOR_INTERFACE:
+ if (!valParser.Parse(pp, pcntdn)) return false;
+ if ((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID)
+ break;
+ if ((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID)
+ break;
+ if (UseOr) {
+ if ((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol))) break;
+ }
+ else if ((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID)
+ break;
+ isGoodInterface = true;
+ ifaceNumber = uid->bInterfaceNumber;
+ ifaceAltSet = uid->bAlternateSetting;
+ protoValue = uid->bInterfaceProtocol;
+ break;
+ case USB_DESCRIPTOR_ENDPOINT:
+ if (!valParser.Parse(pp, pcntdn)) return false;
+ if (isGoodInterface && theXtractor)
+ theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_FD_ENDPOINT_DESCRIPTOR*)varBuffer);
+ break;
+ //case HID_DESCRIPTOR_HID:
+ // if (!valParser.Parse(pp, pcntdn)) return false;
+ // PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);
+ // break;
+ default:
+ if (!theSkipper.Skip(pp, pcntdn, dscrLen - 2)) return false;
+ }
+ theBuffer.pValue = varBuffer;
+ stateParseDescr = 0;
+ }
+ return true;
+}
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {
+ Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80);
+ Notify(PSTR("bDescLength:\t\t"), 0x80);
+ PrintHex<uint8_t > (pDesc->bLength, 0x80);
+
+ Notify(PSTR("\r\nbDescriptorType:\t"), 0x80);
+ PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);
+
+ Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80);
+ PrintHex<uint16_t > (pDesc->bcdHID, 0x80);
+
+ Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80);
+ PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);
+
+ Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80);
+ PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);
+
+ for (uint8_t i = 0; i < pDesc->bNumDescriptors; i++) {
+ HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);
+
+ Notify(PSTR("\r\nbDescrType:\t\t"), 0x80);
+ PrintHex<uint8_t > (pLT[i].bDescrType, 0x80);
+
+ Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80);
+ PrintHex<uint16_t > (pLT[i].wDescriptorLength, 0x80);
+ }
+ Notify(PSTR("\r\n"), 0x80);
+}
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/hexdump.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/hexdump.h
new file mode 100644
index 0000000..c948e5a
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/hexdump.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include hexdump.h directly; include Usb.h instead"
+#endif
+
+extern int UsbDEBUGlvl;
+
+template <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>
+class HexDumper : public BASE_CLASS {
+ uint8_t byteCount;
+ OFFSET_TYPE byteTotal;
+
+public:
+
+ HexDumper() : byteCount(0), byteTotal(0) {
+ };
+
+ void Initialize() {
+ byteCount = 0;
+ byteTotal = 0;
+ };
+
+ void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset);
+};
+
+template <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>
+void HexDumper<BASE_CLASS, LEN_TYPE, OFFSET_TYPE>::Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset __attribute__((unused))) {
+ if (UsbDEBUGlvl >= 0x80) { // Fully bypass this block of code if we do not debug.
+ for (LEN_TYPE j = 0; j < len; j++, byteCount++, byteTotal++) {
+ if (!byteCount) {
+ PrintHex<OFFSET_TYPE > (byteTotal, 0x80);
+ E_Notify(PSTR(": "), 0x80);
+ }
+ PrintHex<uint8_t > (pbuf[j], 0x80);
+ E_Notify(PSTR(" "), 0x80);
+
+ if (byteCount == 15) {
+ E_Notify(PSTR("\r\n"), 0x80);
+ byteCount = 0xFF;
+ }
+ }
+ }
+}
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/macros.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/macros.h
new file mode 100644
index 0000000..8cb5b6b
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/macros.h
@@ -0,0 +1,86 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include macros.h directly; include Usb.h instead"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// HANDY MACROS
+////////////////////////////////////////////////////////////////////////////////
+
+#define VALUE_BETWEEN(v,l,h) (((v)>(l)) && ((v)<(h)))
+#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h)))
+#define output_pgm_message(wa,fp,mp,el) wa = &mp, fp((char *)pgm_read_pointer(wa), el)
+#define output_if_between(v,l,h,wa,fp,mp,el) if (VALUE_BETWEEN(v,l,h)) output_pgm_message(wa,fp,mp[v-(l+1)],el);
+
+#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))
+#ifndef __BYTE_GRABBING_DEFINED__
+#define __BYTE_GRABBING_DEFINED__ 1
+#ifdef BROKEN_OPTIMIZER_LITTLE_ENDIAN
+// Note: Use this if your compiler generates horrible assembler!
+#define BGRAB0(__usi__) (((uint8_t *)&(__usi__))[0])
+#define BGRAB1(__usi__) (((uint8_t *)&(__usi__))[1])
+#define BGRAB2(__usi__) (((uint8_t *)&(__usi__))[2])
+#define BGRAB3(__usi__) (((uint8_t *)&(__usi__))[3])
+#define BGRAB4(__usi__) (((uint8_t *)&(__usi__))[4])
+#define BGRAB5(__usi__) (((uint8_t *)&(__usi__))[5])
+#define BGRAB6(__usi__) (((uint8_t *)&(__usi__))[6])
+#define BGRAB7(__usi__) (((uint8_t *)&(__usi__))[7])
+#else
+// Note: The cast alone to uint8_t is actually enough.
+// GCC throws out the "& 0xFF", and the size is no different.
+// Some compilers need it.
+#define BGRAB0(__usi__) ((uint8_t)((__usi__) & 0xFF ))
+#define BGRAB1(__usi__) ((uint8_t)(((__usi__) >> 8) & 0xFF))
+#define BGRAB2(__usi__) ((uint8_t)(((__usi__) >> 16) & 0xFF))
+#define BGRAB3(__usi__) ((uint8_t)(((__usi__) >> 24) & 0xFF))
+#define BGRAB4(__usi__) ((uint8_t)(((__usi__) >> 32) & 0xFF))
+#define BGRAB5(__usi__) ((uint8_t)(((__usi__) >> 40) & 0xFF))
+#define BGRAB6(__usi__) ((uint8_t)(((__usi__) >> 48) & 0xFF))
+#define BGRAB7(__usi__) ((uint8_t)(((__usi__) >> 56) & 0xFF))
+#endif
+#define BOVER1(__usi__) ((uint16_t)(__usi__) << 8)
+#define BOVER2(__usi__) ((uint32_t)(__usi__) << 16)
+#define BOVER3(__usi__) ((uint32_t)(__usi__) << 24)
+#define BOVER4(__usi__) ((uint64_t)(__usi__) << 32)
+#define BOVER5(__usi__) ((uint64_t)(__usi__) << 40)
+#define BOVER6(__usi__) ((uint64_t)(__usi__) << 48)
+#define BOVER7(__usi__) ((uint64_t)(__usi__) << 56)
+
+// These are the smallest and fastest ways I have found so far in pure C/C++.
+#define BMAKE16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)BOVER1(__usc1__)))
+#define BMAKE32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | (uint32_t)BOVER1(__usc1__) | (uint32_t)BOVER2(__usc2__) | (uint32_t)BOVER3(__usc3__)))
+#define BMAKE64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | (uint64_t)BOVER1(__usc1__) | (uint64_t)BOVER2(__usc2__) | (uint64_t)BOVER3(__usc3__) | (uint64_t)BOVER4(__usc4__) | (uint64_t)BOVER5(__usc5__) | (uint64_t)BOVER6(__usc6__) | (uint64_t)BOVER1(__usc7__)))
+#endif
+
+/*
+ * Debug macros: Strings are stored in progmem (flash) instead of RAM.
+ */
+#define USBTRACE(s) (Notify(PSTR(s), 0x80))
+#define USBTRACE1(s,l) (Notify(PSTR(s), l))
+#define USBTRACE2(s,r) (Notify(PSTR(s), 0x80), D_PrintHex((r), 0x80), Notify(PSTR("\r\n"), 0x80))
+#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR("\r\n"), l))
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.cpp b/Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.cpp
new file mode 100644
index 0000000..a84a683
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.cpp
@@ -0,0 +1,1217 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT) && DISABLED(USE_UHS3_USB)
+
+#include "masstorage.h"
+
+const uint8_t BulkOnly::epDataInIndex = 1;
+const uint8_t BulkOnly::epDataOutIndex = 2;
+const uint8_t BulkOnly::epInterruptInIndex = 3;
+
+////////////////////////////////////////////////////////////////////////////////
+// Interface code
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Get the capacity of the media
+ *
+ * @param lun Logical Unit Number
+ * @return media capacity
+ */
+uint32_t BulkOnly::GetCapacity(uint8_t lun) {
+ return LUNOk[lun] ? CurrentCapacity[lun] : 0UL;
+}
+
+/**
+ * Get the sector (block) size used on the media
+ *
+ * @param lun Logical Unit Number
+ * @return media sector size
+ */
+uint16_t BulkOnly::GetSectorSize(uint8_t lun) {
+ return LUNOk[lun] ? CurrentSectorSize[lun] : 0U;
+}
+
+/**
+ * Test if LUN is ready for use
+ *
+ * @param lun Logical Unit Number
+ * @return true if LUN is ready for use
+ */
+bool BulkOnly::LUNIsGood(uint8_t lun) { return LUNOk[lun]; }
+
+/**
+ * Test if LUN is write protected
+ *
+ * @param lun Logical Unit Number
+ * @return cached status of write protect switch
+ */
+bool BulkOnly::WriteProtected(uint8_t lun) { return WriteOk[lun]; }
+
+/**
+ * Wrap and execute a SCSI CDB with length of 6
+ *
+ * @param cdb CDB to execute
+ * @param buf_size Size of expected transaction
+ * @param buf Buffer
+ * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
+ * @return
+ */
+uint8_t BulkOnly::SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
+ // promote buf_size to 32bits.
+ CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
+ //SetCurLUN(cdb->LUN);
+ return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
+}
+
+/**
+ * Wrap and execute a SCSI CDB with length of 10
+ *
+ * @param cdb CDB to execute
+ * @param buf_size Size of expected transaction
+ * @param buf Buffer
+ * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
+ * @return
+ */
+uint8_t BulkOnly::SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
+ // promote buf_size to 32bits.
+ CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
+ //SetCurLUN(cdb->LUN);
+ return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
+}
+
+/**
+ * Lock or Unlock the tray or door on device.
+ * Caution: Some devices with buggy firmware will lock up.
+ *
+ * @param lun Logical Unit Number
+ * @param lock 1 to lock, 0 to unlock
+ * @return
+ */
+uint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) {
+ Notify(PSTR("\r\nLockMedia\r\n"), 0x80);
+ Notify(PSTR("---------\r\n"), 0x80);
+
+ CDB6_t cdb = CDB6_t(SCSI_CMD_PREVENT_REMOVAL, lun, (uint8_t)0, lock);
+ return SCSITransaction6(&cdb, (uint16_t)0, nullptr, (uint8_t)MASS_CMD_DIR_IN);
+}
+
+/**
+ * Media control, for spindle motor and media tray or door.
+ * This includes CDROM, TAPE and anything with a media loader.
+ *
+ * @param lun Logical Unit Number
+ * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media
+ * @return 0 on success
+ */
+uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) {
+ Notify(PSTR("\r\nMediaCTL\r\n"), 0x80);
+ Notify(PSTR("-----------------\r\n"), 0x80);
+
+ uint8_t rcode = MASS_ERR_UNIT_NOT_READY;
+ if (bAddress) {
+ CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0);
+ rcode = SCSITransaction6(&cdb, (uint16_t)0, nullptr, (uint8_t)MASS_CMD_DIR_OUT);
+ }
+ else
+ SetCurLUN(lun);
+
+ return rcode;
+}
+
+/**
+ * Read data from media
+ *
+ * @param lun Logical Unit Number
+ * @param addr LBA address on media to read
+ * @param bsize size of a block (we should probably use the cached size)
+ * @param blocks how many blocks to read
+ * @param buf memory that is able to hold the requested data
+ * @return 0 on success
+ */
+uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) {
+ if (!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
+ Notify(PSTR("\r\nRead LUN:\t"), 0x80);
+ D_PrintHex<uint8_t> (lun, 0x90);
+ Notify(PSTR("\r\nLBA:\t\t"), 0x90);
+ D_PrintHex<uint32_t> (addr, 0x90);
+ Notify(PSTR("\r\nblocks:\t\t"), 0x90);
+ D_PrintHex<uint8_t> (blocks, 0x90);
+ Notify(PSTR("\r\nblock size:\t"), 0x90);
+ D_PrintHex<uint16_t> (bsize, 0x90);
+ Notify(PSTR("\r\n---------\r\n"), 0x80);
+ CDB10_t cdb = CDB10_t(SCSI_CMD_READ_10, lun, blocks, addr);
+
+again:
+ uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), buf, (uint8_t)MASS_CMD_DIR_IN);
+
+ if (er == MASS_ERR_STALL) {
+ MediaCTL(lun, 1);
+ delay(150);
+ if (!TestUnitReady(lun)) goto again;
+ }
+ return er;
+}
+
+/**
+ * Write data to media
+ *
+ * @param lun Logical Unit Number
+ * @param addr LBA address on media to write
+ * @param bsize size of a block (we should probably use the cached size)
+ * @param blocks how many blocks to write
+ * @param buf memory that contains the data to write
+ * @return 0 on success
+ */
+uint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) {
+ if (!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
+ if (!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED;
+ Notify(PSTR("\r\nWrite LUN:\t"), 0x80);
+ D_PrintHex<uint8_t> (lun, 0x90);
+ Notify(PSTR("\r\nLBA:\t\t"), 0x90);
+ D_PrintHex<uint32_t> (addr, 0x90);
+ Notify(PSTR("\r\nblocks:\t\t"), 0x90);
+ D_PrintHex<uint8_t> (blocks, 0x90);
+ Notify(PSTR("\r\nblock size:\t"), 0x90);
+ D_PrintHex<uint16_t> (bsize, 0x90);
+ Notify(PSTR("\r\n---------\r\n"), 0x80);
+ CDB10_t cdb = CDB10_t(SCSI_CMD_WRITE_10, lun, blocks, addr);
+
+again:
+ uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), (void*)buf, (uint8_t)MASS_CMD_DIR_OUT);
+
+ if (er == MASS_ERR_WRITE_STALL) {
+ MediaCTL(lun, 1);
+ delay(150);
+ if (!TestUnitReady(lun)) goto again;
+ }
+ return er;
+}
+
+// End of user functions, the remaining code below is driver internals.
+// Only developer serviceable parts below!
+
+////////////////////////////////////////////////////////////////////////////////
+// Main driver code
+////////////////////////////////////////////////////////////////////////////////
+
+BulkOnly::BulkOnly(USB *p) :
+pUsb(p),
+bAddress(0),
+bIface(0),
+bNumEP(1),
+qNextPollTime(0),
+bPollEnable(false),
+//dCBWTag(0),
+bLastUsbError(0) {
+ ClearAllEP();
+ dCBWTag = 0;
+ if (pUsb) pUsb->RegisterDeviceClass(this);
+}
+
+/**
+ * USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET == success
+ * We need to standardize either the rcode, or change the API to return values
+ * so a signal that additional actions are required can be produced.
+ * Some of these codes do exist already.
+ *
+ * TECHNICAL: We could do most of this code elsewhere, with the exception of checking the class instance.
+ * Doing so would save some program memory when using multiple drivers.
+ *
+ * @param parent USB address of parent
+ * @param port address of port on parent
+ * @param lowspeed true if device is low speed
+ * @return
+ */
+uint8_t BulkOnly::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
+
+ const uint8_t constBufSize = sizeof (USB_FD_DEVICE_DESCRIPTOR);
+
+ uint8_t buf[constBufSize];
+ USB_FD_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_FD_DEVICE_DESCRIPTOR*>(buf);
+ uint8_t rcode;
+ UsbDevice *p = nullptr;
+ EpInfo *oldep_ptr = nullptr;
+ USBTRACE("MS ConfigureDevice\r\n");
+ ClearAllEP();
+ AddressPool &addrPool = pUsb->GetAddressPool();
+
+ if (bAddress) return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+ // <TECHNICAL>
+ // Get pointer to pseudo device with address 0 assigned
+ p = addrPool.GetUsbDevicePtr(0);
+ if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+ if (!p->epinfo) {
+ USBTRACE("epinfo\r\n");
+ return USB_ERROR_EPINFO_IS_NULL;
+ }
+
+ // Save old pointer to EP_RECORD of address 0
+ oldep_ptr = p->epinfo;
+
+ // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+ p->epinfo = epInfo;
+
+ p->lowspeed = lowspeed;
+ // Get device descriptor
+ rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);
+
+ // Restore p->epinfo
+ p->epinfo = oldep_ptr;
+
+ if (rcode) goto FailGetDevDescr;
+
+ // Allocate new address according to device class
+ bAddress = addrPool.AllocAddress(parent, false, port);
+
+ if (!bAddress) return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+ // Extract Max Packet Size from the device descriptor
+ epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+ // Steal and abuse from epInfo structure to save on memory.
+ epInfo[1].epAddr = udd->bNumConfigurations;
+ // </TECHNICAL>
+ return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
+
+FailGetDevDescr:
+
+ #ifdef DEBUG_USB_HOST
+ NotifyFailGetDevDescr(rcode);
+ #endif
+ rcode = USB_ERROR_FailGetDevDescr;
+
+ Release();
+ return rcode;
+}
+
+/**
+ * @param parent (not used)
+ * @param port (not used)
+ * @param lowspeed true if device is low speed
+ * @return 0 for success
+ */
+uint8_t BulkOnly::Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed) {
+ uint8_t rcode;
+ uint8_t num_of_conf = epInfo[1].epAddr; // number of configurations
+ epInfo[1].epAddr = 0;
+ USBTRACE("MS Init\r\n");
+
+ AddressPool &addrPool = pUsb->GetAddressPool();
+ UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress);
+
+ if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+ // Assign new address to the device
+ delay(2000);
+ rcode = pUsb->setAddr(0, 0, bAddress);
+
+ if (rcode) {
+ p->lowspeed = false;
+ addrPool.FreeAddress(bAddress);
+ bAddress = 0;
+ USBTRACE2("setAddr:", rcode);
+ return rcode;
+ }
+
+ USBTRACE2("Addr:", bAddress);
+
+ p->lowspeed = false;
+
+ p = addrPool.GetUsbDevicePtr(bAddress);
+
+ if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+ p->lowspeed = lowspeed;
+
+ // Assign epInfo to epinfo pointer
+ rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+ if (rcode) goto FailSetDevTblEntry;
+
+ USBTRACE2("NC:", num_of_conf);
+
+ for (uint8_t i = 0; i < num_of_conf; i++) {
+ ConfigDescParser< USB_CLASS_MASS_STORAGE,
+ MASS_SUBCLASS_SCSI,
+ MASS_PROTO_BBB,
+ CP_MASK_COMPARE_CLASS |
+ CP_MASK_COMPARE_SUBCLASS |
+ CP_MASK_COMPARE_PROTOCOL > BulkOnlyParser(this);
+
+ rcode = pUsb->getConfDescr(bAddress, 0, i, &BulkOnlyParser);
+
+ if (rcode) goto FailGetConfDescr;
+
+ if (bNumEP > 1) break;
+ }
+
+ if (bNumEP < 3) return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+ // Assign epInfo to epinfo pointer
+ pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+ USBTRACE2("Conf:", bConfNum);
+
+ // Set Configuration Value
+ rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+ if (rcode) goto FailSetConfDescr;
+
+ //Linux does a 1sec delay after this.
+ delay(1000);
+
+ rcode = GetMaxLUN(&bMaxLUN);
+ if (rcode) goto FailGetMaxLUN;
+
+ if (bMaxLUN >= MASS_MAX_SUPPORTED_LUN) bMaxLUN = MASS_MAX_SUPPORTED_LUN - 1;
+ ErrorMessage<uint8_t> (PSTR("MaxLUN"), bMaxLUN);
+
+ delay(1000); // Delay a bit for slow firmware.
+
+ for (uint8_t lun = 0; lun <= bMaxLUN; lun++) {
+ InquiryResponse response;
+ rcode = Inquiry(lun, sizeof (InquiryResponse), (uint8_t*) & response);
+ if (rcode) {
+ ErrorMessage<uint8_t> (PSTR("Inquiry"), rcode);
+ }
+ else {
+ #if 0
+ printf("LUN %i `", lun);
+ uint8_t *buf = response.VendorID;
+ for (int i = 0; i < 28; i++) printf("%c", buf[i]);
+ printf("'\r\nQualifier %1.1X ", response.PeripheralQualifier);
+ printf("Device type %2.2X ", response.DeviceType);
+ printf("RMB %1.1X ", response.Removable);
+ printf("SSCS %1.1X ", response.SCCS);
+ uint8_t sv = response.Version;
+ printf("SCSI version %2.2X\r\nDevice conforms to ", sv);
+ switch (sv) {
+ case 0:
+ printf("No specific");
+ break;
+ case 1:
+ printf("ANSI X3.131-1986 (ANSI 1)");
+ break;
+ case 2:
+ printf("ANSI X3.131-1994 (ANSI 2)");
+ break;
+ case 3:
+ printf("ANSI INCITS 301-1997 (SPC)");
+ break;
+ case 4:
+ printf("ANSI INCITS 351-2001 (SPC-2)");
+ break;
+ case 5:
+ printf("ANSI INCITS 408-2005 (SPC-4)");
+ break;
+ case 6:
+ printf("T10/1731-D (SPC-4)");
+ break;
+ default: printf("unknown");
+ }
+ printf(" standards.\r\n");
+ #endif
+
+ uint8_t tries = 0xF0;
+ while ((rcode = TestUnitReady(lun))) {
+ if (rcode == 0x08) break; // break on no media, this is OK to do.
+ // try to lock media and spin up
+ if (tries < 14) {
+ LockMedia(lun, 1);
+ MediaCTL(lun, 1); // I actually have a USB stick that needs this!
+ } else
+ delay(2 * (tries + 1));
+ tries++;
+ if (!tries) break;
+ }
+ if (!rcode) {
+ delay(1000);
+ LUNOk[lun] = CheckLUN(lun);
+ if (!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);
+ }
+ }
+ }
+
+ CheckMedia();
+
+ rcode = OnInit();
+
+ if (rcode) goto FailOnInit;
+
+ #ifdef DEBUG_USB_HOST
+ USBTRACE("MS configured\r\n\r\n");
+ #endif
+
+ bPollEnable = true;
+
+ //USBTRACE("Poll enabled\r\n");
+ return 0;
+
+ FailSetConfDescr:
+
+ #ifdef DEBUG_USB_HOST
+ NotifyFailSetConfDescr();
+ goto Fail;
+ #endif
+
+ FailOnInit:
+
+ #ifdef DEBUG_USB_HOST
+ USBTRACE("OnInit:");
+ goto Fail;
+ #endif
+
+ FailGetMaxLUN:
+
+ #ifdef DEBUG_USB_HOST
+ USBTRACE("GetMaxLUN:");
+ goto Fail;
+ #endif
+
+ //#ifdef DEBUG_USB_HOST
+ // FailInvalidSectorSize:
+ // USBTRACE("Sector Size is NOT VALID: ");
+ // goto Fail;
+ //#endif
+
+ FailSetDevTblEntry:
+ #ifdef DEBUG_USB_HOST
+ NotifyFailSetDevTblEntry();
+ goto Fail;
+ #endif
+
+ FailGetConfDescr:
+ #ifdef DEBUG_USB_HOST
+ NotifyFailGetConfDescr();
+ #endif
+
+ #ifdef DEBUG_USB_HOST
+ Fail:
+ NotifyFail(rcode);
+ #endif
+ Release();
+ return rcode;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param conf
+ * @param iface
+ * @param alt
+ * @param proto
+ * @param pep
+ */
+void BulkOnly::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto __attribute__((unused)), const USB_FD_ENDPOINT_DESCRIPTOR * pep) {
+ ErrorMessage<uint8_t> (PSTR("Conf.Val"), conf);
+ ErrorMessage<uint8_t> (PSTR("Iface Num"), iface);
+ ErrorMessage<uint8_t> (PSTR("Alt.Set"), alt);
+
+ bConfNum = conf;
+
+ uint8_t index;
+
+ #if 1
+ if ((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK) {
+ index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+ // Fill in the endpoint info structure
+ epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+ epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+ epInfo[index].bmSndToggle = 0;
+ epInfo[index].bmRcvToggle = 0;
+
+ bNumEP++;
+
+ PrintEndpointDescriptor(pep);
+ }
+ #else
+ if ((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80)
+ index = epInterruptInIndex;
+ else if ((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK)
+ index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+ else
+ return;
+
+ // Fill in the endpoint info structure
+ epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+ epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+ epInfo[index].bmSndToggle = 0;
+ epInfo[index].bmRcvToggle = 0;
+
+ bNumEP++;
+
+ PrintEndpointDescriptor(pep);
+ #endif
+}
+
+/**
+ * For driver use only.
+ *
+ * @return
+ */
+uint8_t BulkOnly::Release() {
+ ClearAllEP();
+ pUsb->GetAddressPool().FreeAddress(bAddress);
+ return 0;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @return true if LUN is ready for use.
+ */
+bool BulkOnly::CheckLUN(uint8_t lun) {
+ uint8_t rcode;
+ Capacity capacity;
+ for (uint8_t i = 0; i < 8; i++) capacity.data[i] = 0;
+
+ rcode = ReadCapacity10(lun, (uint8_t*)capacity.data);
+ if (rcode) {
+ //printf(">>>>>>>>>>>>>>>>ReadCapacity returned %i\r\n", rcode);
+ return false;
+ }
+ ErrorMessage<uint8_t> (PSTR(">>>>>>>>>>>>>>>>CAPACITY OK ON LUN"), lun);
+ for (uint8_t i = 0; i < 8 /*sizeof (Capacity)*/; i++)
+ D_PrintHex<uint8_t> (capacity.data[i], 0x80);
+ Notify(PSTR("\r\n\r\n"), 0x80);
+
+ // Only 512/1024/2048/4096 are valid values!
+ uint32_t c = BMAKE32(capacity.data[4], capacity.data[5], capacity.data[6], capacity.data[7]);
+ if (c != 0x0200UL && c != 0x0400UL && c != 0x0800UL && c != 0x1000UL) return false;
+
+ // Store capacity information.
+ CurrentSectorSize[lun] = (uint16_t)(c); // & 0xFFFF);
+
+ CurrentCapacity[lun] = BMAKE32(capacity.data[0], capacity.data[1], capacity.data[2], capacity.data[3]) + 1;
+ if (CurrentCapacity[lun] == /*0xFFFFFFFFUL */ 0x01UL || CurrentCapacity[lun] == 0x00UL) {
+ // Buggy firmware will report 0xFFFFFFFF or 0 for no media
+ if (CurrentCapacity[lun])
+ ErrorMessage<uint8_t> (PSTR(">>>>>>>>>>>>>>>>BUGGY FIRMWARE. CAPACITY FAIL ON LUN"), lun);
+ return false;
+ }
+ delay(20);
+ Page3F(lun);
+ return !TestUnitReady(lun);
+}
+
+/**
+ * For driver use only.
+ *
+ * Scan for media change on all LUNs
+ */
+void BulkOnly::CheckMedia() {
+ for (uint8_t lun = 0; lun <= bMaxLUN; lun++) {
+ if (TestUnitReady(lun)) {
+ LUNOk[lun] = false;
+ continue;
+ }
+ if (!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);
+ }
+ #if 0
+ printf("}}}}}}}}}}}}}}}}STATUS ");
+ for (uint8_t lun = 0; lun <= bMaxLUN; lun++)
+ printf(LUNOk[lun] ? "#" : ".");
+ printf("\r\n");
+ #endif
+ qNextPollTime = (uint32_t)millis() + 2000;
+}
+
+/**
+ * For driver use only.
+ *
+ * @return
+ */
+uint8_t BulkOnly::Poll() {
+ //uint8_t rcode = 0;
+ if (!bPollEnable) return 0;
+ if ((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) CheckMedia();
+ //rcode = 0;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SCSI code
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * For driver use only.
+ *
+ * @param plun
+ * @return
+ */
+uint8_t BulkOnly::GetMaxLUN(uint8_t *plun) {
+ uint8_t ret = pUsb->ctrlReq(bAddress, 0, bmREQ_MASSIN, MASS_REQ_GET_MAX_LUN, 0, 0, bIface, 1, 1, plun, nullptr);
+
+ if (ret == hrSTALL)
+ *plun = 0;
+
+ return 0;
+}
+
+/**
+ * For driver use only. Used during Driver Init
+ *
+ * @param lun Logical Unit Number
+ * @param bsize
+ * @param buf
+ * @return
+ */
+uint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) {
+ Notify(PSTR("\r\nInquiry\r\n"), 0x80);
+ Notify(PSTR("---------\r\n"), 0x80);
+
+ CDB6_t cdb = CDB6_t(SCSI_CMD_INQUIRY, lun, 0UL, (uint8_t)bsize, 0);
+ uint8_t rc = SCSITransaction6(&cdb, bsize, buf, (uint8_t)MASS_CMD_DIR_IN);
+
+ return rc;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @return
+ */
+uint8_t BulkOnly::TestUnitReady(uint8_t lun) {
+ //SetCurLUN(lun);
+ if (!bAddress)
+ return MASS_ERR_UNIT_NOT_READY;
+
+ Notify(PSTR("\r\nTestUnitReady\r\n"), 0x80);
+ Notify(PSTR("-----------------\r\n"), 0x80);
+
+ CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint8_t)0, 0);
+ return SCSITransaction6(&cdb, 0, nullptr, (uint8_t)MASS_CMD_DIR_IN);
+
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @param pc
+ * @param page
+ * @param subpage
+ * @param len
+ * @param pbuf
+ * @return
+ */
+uint8_t BulkOnly::ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t * pbuf) {
+ Notify(PSTR("\r\rModeSense\r\n"), 0x80);
+ Notify(PSTR("------------\r\n"), 0x80);
+
+ CDB6_t cdb = CDB6_t(SCSI_CMD_MODE_SENSE_6, lun, (uint32_t)((((pc << 6) | page) << 8) | subpage), len, 0);
+ return SCSITransaction6(&cdb, len, pbuf, (uint8_t)MASS_CMD_DIR_IN);
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @param bsize
+ * @param buf
+ * @return
+ */
+uint8_t BulkOnly::ReadCapacity10(uint8_t lun, uint8_t *buf) {
+ Notify(PSTR("\r\nReadCapacity\r\n"), 0x80);
+ Notify(PSTR("---------------\r\n"), 0x80);
+
+ CDB10_t cdb = CDB10_t(SCSI_CMD_READ_CAPACITY_10, lun);
+ return SCSITransaction10(&cdb, 8, buf, (uint8_t)MASS_CMD_DIR_IN);
+}
+
+/**
+ * For driver use only.
+ *
+ * Page 3F contains write protect status.
+ *
+ * @param lun Logical Unit Number to test.
+ * @return Write protect switch status.
+ */
+uint8_t BulkOnly::Page3F(uint8_t lun) {
+ uint8_t buf[192];
+ for (int i = 0; i < 192; i++) {
+ buf[i] = 0x00;
+ }
+ WriteOk[lun] = true;
+ #ifdef SKIP_WRITE_PROTECT
+ return 0;
+ #endif
+ uint8_t rc = ModeSense6(lun, 0, 0x3F, 0, 192, buf);
+ if (!rc) {
+ WriteOk[lun] = ((buf[2] & 0x80) == 0);
+ Notify(PSTR("Mode Sense: "), 0x80);
+ for (int i = 0; i < 4; i++) {
+ D_PrintHex<uint8_t> (buf[i], 0x80);
+ Notify(PSTR(" "), 0x80);
+ }
+ Notify(PSTR("\r\n"), 0x80);
+ }
+ return rc;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @param size
+ * @param buf
+ * @return
+ */
+uint8_t BulkOnly::RequestSense(uint8_t lun, uint16_t size, uint8_t *buf) {
+ Notify(PSTR("\r\nRequestSense\r\n"), 0x80);
+ Notify(PSTR("----------------\r\n"), 0x80);
+
+ CDB6_t cdb = CDB6_t(SCSI_CMD_REQUEST_SENSE, lun, 0UL, (uint8_t)size, 0);
+ CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)size, &cdb, (uint8_t)MASS_CMD_DIR_IN);
+ //SetCurLUN(lun);
+ return Transaction(&cbw, size, buf);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// USB code
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * For driver use only.
+ *
+ * @param index
+ * @return
+ */
+uint8_t BulkOnly::ClearEpHalt(uint8_t index) {
+ if (index == 0)
+ return 0;
+
+ uint8_t ret = 0;
+
+ while ((ret = (pUsb->ctrlReq(bAddress, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr), 0, 0, nullptr, nullptr)) == 0x01))
+ delay(6);
+
+ if (ret) {
+ ErrorMessage<uint8_t> (PSTR("ClearEpHalt"), ret);
+ ErrorMessage<uint8_t> (PSTR("EP"), ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr));
+ return ret;
+ }
+ epInfo[index].bmSndToggle = 0;
+ epInfo[index].bmRcvToggle = 0;
+ return 0;
+}
+
+/**
+ * For driver use only.
+ */
+void BulkOnly::Reset() {
+ while (pUsb->ctrlReq(bAddress, 0, bmREQ_MASSOUT, MASS_REQ_BOMSR, 0, 0, bIface, 0, 0, nullptr, nullptr) == 0x01) delay(6);
+}
+
+/**
+ * For driver use only.
+ *
+ * @return 0 if successful
+ */
+uint8_t BulkOnly::ResetRecovery() {
+ Notify(PSTR("\r\nResetRecovery\r\n"), 0x80);
+ Notify(PSTR("-----------------\r\n"), 0x80);
+
+ delay(6);
+ Reset();
+ delay(6);
+ ClearEpHalt(epDataInIndex);
+ delay(6);
+ bLastUsbError = ClearEpHalt(epDataOutIndex);
+ delay(6);
+ return bLastUsbError;
+}
+
+/**
+ * For driver use only.
+ *
+ * Clear all EP data and clear all LUN status
+ */
+void BulkOnly::ClearAllEP() {
+ for (uint8_t i = 0; i < MASS_MAX_ENDPOINTS; i++) {
+ epInfo[i].epAddr = 0;
+ epInfo[i].maxPktSize = (i) ? 0 : 8;
+ epInfo[i].bmSndToggle = 0;
+ epInfo[i].bmRcvToggle = 0;
+ epInfo[i].bmNakPower = USB_NAK_DEFAULT;
+ }
+
+ for (uint8_t i = 0; i < MASS_MAX_SUPPORTED_LUN; i++) {
+ LUNOk[i] = false;
+ WriteOk[i] = false;
+ CurrentCapacity[i] = 0UL;
+ CurrentSectorSize[i] = 0;
+ }
+
+ bIface = 0;
+ bNumEP = 1;
+ bAddress = 0;
+ qNextPollTime = 0;
+ bPollEnable = false;
+ bLastUsbError = 0;
+ bMaxLUN = 0;
+ bTheLUN = 0;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param pcsw
+ * @param pcbw
+ * @return
+ */
+bool BulkOnly::IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw) {
+ if (pcsw->dCSWSignature != MASS_CSW_SIGNATURE) {
+ Notify(PSTR("CSW:Sig error\r\n"), 0x80);
+ return false;
+ }
+ if (pcsw->dCSWTag != pcbw->dCBWTag) {
+ Notify(PSTR("CSW:Wrong tag\r\n"), 0x80);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param error
+ * @param index
+ * @return
+ */
+uint8_t BulkOnly::HandleUsbError(uint8_t error, uint8_t index) {
+ uint8_t count = 3;
+
+ bLastUsbError = error;
+ //if (error)
+ //ClearEpHalt(index);
+ while (error && count) {
+ if (error != hrSUCCESS) {
+ ErrorMessage<uint8_t> (PSTR("USB Error"), error);
+ ErrorMessage<uint8_t> (PSTR("Index"), index);
+ }
+ switch (error) {
+ // case hrWRONGPID:
+ case hrSUCCESS: return MASS_ERR_SUCCESS;
+ case hrBUSY: return MASS_ERR_UNIT_BUSY; // SIE is busy, just hang out and try again.
+ case hrTIMEOUT:
+ case hrJERR: return MASS_ERR_DEVICE_DISCONNECTED;
+ case hrSTALL:
+ if (index) {
+ ClearEpHalt(index);
+ return (index == epDataInIndex) ? MASS_ERR_STALL : MASS_ERR_WRITE_STALL;
+ }
+ return MASS_ERR_STALL;
+
+ case hrNAK:
+ return index ? MASS_ERR_UNIT_BUSY : MASS_ERR_UNIT_BUSY;
+
+ case hrTOGERR:
+ // Handle a super rare corner case, where toggles become de-synced.
+ // I've only run into one device that has this firmware bug, and this is
+ // the only clean way to get back into sync with the buggy device firmware.
+ // --AJK
+ if (bAddress && bConfNum) {
+ error = pUsb->setConf(bAddress, 0, bConfNum);
+ if (error) break;
+ }
+ return MASS_ERR_SUCCESS;
+ default:
+ ErrorMessage<uint8_t> (PSTR("\r\nUSB"), error);
+ return MASS_ERR_GENERAL_USB_ERROR;
+ }
+ count--;
+ } // while
+
+ return ((error && !count) ? MASS_ERR_GENERAL_USB_ERROR : MASS_ERR_SUCCESS);
+}
+
+#if MS_WANT_PARSER
+ uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf) {
+ return Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf, 0);
+ }
+#endif
+
+/**
+ * For driver use only.
+ *
+ * @param pcbw
+ * @param buf_size
+ * @param buf
+ * @param flags
+ * @return
+ */
+uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf
+ #if MS_WANT_PARSER
+ , uint8_t flags
+ #endif
+) {
+ #if MS_WANT_PARSER
+ uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength;
+ printf("Transfersize %i\r\n", bytes);
+ delay(1000);
+
+ bool callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK;
+ #else
+ uint16_t bytes = buf_size;
+ #endif
+
+ bool write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN;
+ uint8_t ret = 0;
+ uint8_t usberr;
+ CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles.
+ SetCurLUN(pcbw->bmCBWLUN);
+ ErrorMessage<uint32_t> (PSTR("CBW.dCBWTag"), pcbw->dCBWTag);
+
+ while ((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) delay(1);
+
+ ret = HandleUsbError(usberr, epDataOutIndex);
+ //ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex);
+ if (ret)
+ ErrorMessage<uint8_t> (PSTR("============================ CBW"), ret);
+ else {
+ if (bytes) {
+ if (!write) {
+ #if MS_WANT_PARSER
+ if (callback) {
+ uint8_t rbuf[bytes];
+ while ((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) delay(1);
+ if (usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0);
+ }
+ else
+ #endif
+ {
+ while ((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) delay(1);
+ }
+ ret = HandleUsbError(usberr, epDataInIndex);
+ }
+ else {
+ while ((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) delay(1);
+ ret = HandleUsbError(usberr, epDataOutIndex);
+ }
+ if (ret) ErrorMessage<uint8_t> (PSTR("============================ DAT"), ret);
+ }
+ }
+
+ bytes = sizeof (CommandStatusWrapper);
+ int tries = 2;
+ while (tries--) {
+ while ((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) delay(1);
+ if (!usberr) break;
+ ClearEpHalt(epDataInIndex);
+ if (tries) ResetRecovery();
+ }
+
+ if (!ret) {
+ Notify(PSTR("CBW:\t\tOK\r\n"), 0x80);
+ Notify(PSTR("Data Stage:\tOK\r\n"), 0x80);
+ }
+ else {
+ // Throw away csw, IT IS NOT OF ANY USE.
+ ResetRecovery();
+ return ret;
+ }
+
+ ret = HandleUsbError(usberr, epDataInIndex);
+ if (ret) ErrorMessage<uint8_t> (PSTR("============================ CSW"), ret);
+
+ if (usberr == hrSUCCESS) {
+ if (IsValidCSW(&csw, pcbw)) {
+ //ErrorMessage<uint32_t> (PSTR("CSW.dCBWTag"), csw.dCSWTag);
+ //ErrorMessage<uint8_t> (PSTR("bCSWStatus"), csw.bCSWStatus);
+ //ErrorMessage<uint32_t> (PSTR("dCSWDataResidue"), csw.dCSWDataResidue);
+ Notify(PSTR("CSW:\t\tOK\r\n\r\n"), 0x80);
+ return csw.bCSWStatus;
+ }
+ else {
+ // NOTE! Sometimes this is caused by the reported residue being wrong.
+ // Get a different device. It isn't compliant, and should have never passed Q&A.
+ // I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter.
+ // Other devices that exhibit this behavior exist in the wild too.
+ // Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk
+ Notify(PSTR("Invalid CSW\r\n"), 0x80);
+ ResetRecovery();
+ //return MASS_ERR_SUCCESS;
+ return MASS_ERR_INVALID_CSW;
+ }
+ }
+ return ret;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @return
+ */
+uint8_t BulkOnly::SetCurLUN(uint8_t lun) {
+ if (lun > bMaxLUN) return MASS_ERR_INVALID_LUN;
+ bTheLUN = lun;
+ return MASS_ERR_SUCCESS;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param status
+ * @return
+ */
+uint8_t BulkOnly::HandleSCSIError(uint8_t status) {
+ uint8_t ret = 0;
+
+ switch (status) {
+ case 0: return MASS_ERR_SUCCESS;
+
+ case 2:
+ ErrorMessage<uint8_t> (PSTR("Phase Error"), status);
+ ErrorMessage<uint8_t> (PSTR("LUN"), bTheLUN);
+ ResetRecovery();
+ return MASS_ERR_GENERAL_SCSI_ERROR;
+
+ case 1:
+ ErrorMessage<uint8_t> (PSTR("SCSI Error"), status);
+ ErrorMessage<uint8_t> (PSTR("LUN"), bTheLUN);
+ RequestSenseResponce rsp;
+
+ ret = RequestSense(bTheLUN, sizeof (RequestSenseResponce), (uint8_t*) & rsp);
+
+ if (ret) return MASS_ERR_GENERAL_SCSI_ERROR;
+
+ ErrorMessage<uint8_t> (PSTR("Response Code"), rsp.bResponseCode);
+ if (rsp.bResponseCode & 0x80) {
+ Notify(PSTR("Information field: "), 0x80);
+ for (int i = 0; i < 4; i++) {
+ D_PrintHex<uint8_t> (rsp.CmdSpecificInformation[i], 0x80);
+ Notify(PSTR(" "), 0x80);
+ }
+ Notify(PSTR("\r\n"), 0x80);
+ }
+ ErrorMessage<uint8_t> (PSTR("Sense Key"), rsp.bmSenseKey);
+ ErrorMessage<uint8_t> (PSTR("Add Sense Code"), rsp.bAdditionalSenseCode);
+ ErrorMessage<uint8_t> (PSTR("Add Sense Qual"), rsp.bAdditionalSenseQualifier);
+ // warning, this is not testing ASQ, only SK and ASC.
+ switch (rsp.bmSenseKey) {
+ case SCSI_S_UNIT_ATTENTION:
+ switch (rsp.bAdditionalSenseCode) {
+ case SCSI_ASC_MEDIA_CHANGED:
+ return MASS_ERR_MEDIA_CHANGED;
+ default:
+ return MASS_ERR_UNIT_NOT_READY;
+ }
+ case SCSI_S_NOT_READY:
+ switch (rsp.bAdditionalSenseCode) {
+ case SCSI_ASC_MEDIUM_NOT_PRESENT:
+ return MASS_ERR_NO_MEDIA;
+ default:
+ return MASS_ERR_UNIT_NOT_READY;
+ }
+ case SCSI_S_ILLEGAL_REQUEST:
+ switch (rsp.bAdditionalSenseCode) {
+ case SCSI_ASC_LBA_OUT_OF_RANGE:
+ return MASS_ERR_BAD_LBA;
+ default:
+ return MASS_ERR_CMD_NOT_SUPPORTED;
+ }
+ default:
+ return MASS_ERR_GENERAL_SCSI_ERROR;
+ }
+
+ // case 4: return MASS_ERR_UNIT_BUSY; // Busy means retry later.
+ // case 0x05/0x14: we stalled out
+ // case 0x15/0x16: we naked out.
+ default:
+ ErrorMessage<uint8_t> (PSTR("Gen SCSI Err"), status);
+ ErrorMessage<uint8_t> (PSTR("LUN"), bTheLUN);
+ return status;
+ } // switch
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Debugging code
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @param ep_ptr
+ */
+void BulkOnly::PrintEndpointDescriptor(const USB_FD_ENDPOINT_DESCRIPTOR * ep_ptr) {
+ Notify(PSTR("Endpoint descriptor:"), 0x80);
+ Notify(PSTR("\r\nLength:\t\t"), 0x80);
+ D_PrintHex<uint8_t> (ep_ptr->bLength, 0x80);
+ Notify(PSTR("\r\nType:\t\t"), 0x80);
+ D_PrintHex<uint8_t> (ep_ptr->bDescriptorType, 0x80);
+ Notify(PSTR("\r\nAddress:\t"), 0x80);
+ D_PrintHex<uint8_t> (ep_ptr->bEndpointAddress, 0x80);
+ Notify(PSTR("\r\nAttributes:\t"), 0x80);
+ D_PrintHex<uint8_t> (ep_ptr->bmAttributes, 0x80);
+ Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+ D_PrintHex<uint16_t> (ep_ptr->wMaxPacketSize, 0x80);
+ Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+ D_PrintHex<uint8_t> (ep_ptr->bInterval, 0x80);
+ Notify(PSTR("\r\n"), 0x80);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// misc/to kill/to-do
+////////////////////////////////////////////////////////////////////////////////
+
+/* We won't be needing this... */
+uint8_t BulkOnly::Read(uint8_t lun __attribute__((unused)), uint32_t addr __attribute__((unused)), uint16_t bsize __attribute__((unused)), uint8_t blocks __attribute__((unused)), USBReadParser * prs __attribute__((unused))) {
+ #if MS_WANT_PARSER
+ if (!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
+ Notify(PSTR("\r\nRead (With parser)\r\n"), 0x80);
+ Notify(PSTR("---------\r\n"), 0x80);
+
+ CommandBlockWrapper cbw = CommandBlockWrapper();
+
+ cbw.dCBWSignature = MASS_CBW_SIGNATURE;
+ cbw.dCBWTag = ++dCBWTag;
+ cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks);
+ cbw.bmCBWFlags = MASS_CMD_DIR_IN,
+ cbw.bmCBWLUN = lun;
+ cbw.bmCBWCBLength = 10;
+
+ cbw.CBWCB[0] = SCSI_CMD_READ_10;
+ cbw.CBWCB[8] = blocks;
+ cbw.CBWCB[2] = ((addr >> 24) & 0xFF);
+ cbw.CBWCB[3] = ((addr >> 16) & 0xFF);
+ cbw.CBWCB[4] = ((addr >> 8) & 0xFF);
+ cbw.CBWCB[5] = (addr & 0xFF);
+
+ return HandleSCSIError(Transaction(&cbw, bsize, prs, 1));
+ #else
+ return MASS_ERR_NOT_IMPLEMENTED;
+ #endif
+}
+
+#endif // USB_FLASH_DRIVE_SUPPORT
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.h
new file mode 100644
index 0000000..25df006
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/masstorage.h
@@ -0,0 +1,562 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#pragma once
+
+// Cruft removal, makes driver smaller, faster.
+#ifndef MS_WANT_PARSER
+ #define MS_WANT_PARSER 0
+#endif
+
+#include "Usb.h"
+
+#define bmREQ_MASSOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+#define bmREQ_MASSIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+
+// Mass Storage Subclass Constants
+#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00 // De facto use
+#define MASS_SUBCLASS_RBC 0x01
+#define MASS_SUBCLASS_ATAPI 0x02 // MMC-5 (ATAPI)
+#define MASS_SUBCLASS_OBSOLETE1 0x03 // Was QIC-157
+#define MASS_SUBCLASS_UFI 0x04 // Specifies how to interface Floppy Disk Drives to USB
+#define MASS_SUBCLASS_OBSOLETE2 0x05 // Was SFF-8070i
+#define MASS_SUBCLASS_SCSI 0x06 // SCSI Transparent Command Set
+#define MASS_SUBCLASS_LSDFS 0x07 // Specifies how host has to negotiate access before trying SCSI
+#define MASS_SUBCLASS_IEEE1667 0x08
+
+// Mass Storage Class Protocols
+#define MASS_PROTO_CBI 0x00 // CBI (with command completion interrupt)
+#define MASS_PROTO_CBI_NO_INT 0x01 // CBI (without command completion interrupt)
+#define MASS_PROTO_OBSOLETE 0x02
+#define MASS_PROTO_BBB 0x50 // Bulk Only Transport
+#define MASS_PROTO_UAS 0x62
+
+// Request Codes
+#define MASS_REQ_ADSC 0x00
+#define MASS_REQ_GET 0xFC
+#define MASS_REQ_PUT 0xFD
+#define MASS_REQ_GET_MAX_LUN 0xFE
+#define MASS_REQ_BOMSR 0xFF // Bulk-Only Mass Storage Reset
+
+#define MASS_CBW_SIGNATURE 0x43425355
+#define MASS_CSW_SIGNATURE 0x53425355
+
+#define MASS_CMD_DIR_OUT 0 // (0 << 7)
+#define MASS_CMD_DIR_IN 0x80 //(1 << 7)
+
+/*
+ * Reference documents from T10 (https://www.t10.org)
+ * SCSI Primary Commands - 3 (SPC-3)
+ * SCSI Block Commands - 2 (SBC-2)
+ * Multi-Media Commands - 5 (MMC-5)
+ */
+
+/* Group 1 commands (CDB's here are should all be 6-bytes) */
+#define SCSI_CMD_TEST_UNIT_READY 0x00
+#define SCSI_CMD_REQUEST_SENSE 0x03
+#define SCSI_CMD_FORMAT_UNIT 0x04
+#define SCSI_CMD_READ_6 0x08
+#define SCSI_CMD_WRITE_6 0x0A
+#define SCSI_CMD_INQUIRY 0x12
+#define SCSI_CMD_MODE_SELECT_6 0x15
+#define SCSI_CMD_MODE_SENSE_6 0x1A
+#define SCSI_CMD_START_STOP_UNIT 0x1B
+#define SCSI_CMD_PREVENT_REMOVAL 0x1E
+/* Group 2 Commands (CDB's here are 10-bytes) */
+#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23
+#define SCSI_CMD_READ_CAPACITY_10 0x25
+#define SCSI_CMD_READ_10 0x28
+#define SCSI_CMD_WRITE_10 0x2A
+#define SCSI_CMD_SEEK_10 0x2B
+#define SCSI_CMD_ERASE_10 0x2C
+#define SCSI_CMD_WRITE_AND_VERIFY_10 0x2E
+#define SCSI_CMD_VERIFY_10 0x2F
+#define SCSI_CMD_SYNCHRONIZE_CACHE 0x35
+#define SCSI_CMD_WRITE_BUFFER 0x3B
+#define SCSI_CMD_READ_BUFFER 0x3C
+#define SCSI_CMD_READ_SUBCHANNEL 0x42
+#define SCSI_CMD_READ_TOC 0x43
+#define SCSI_CMD_READ_HEADER 0x44
+#define SCSI_CMD_PLAY_AUDIO_10 0x45
+#define SCSI_CMD_GET_CONFIGURATION 0x46
+#define SCSI_CMD_PLAY_AUDIO_MSF 0x47
+#define SCSI_CMD_PLAY_AUDIO_TI 0x48
+#define SCSI_CMD_PLAY_TRACK_REL_10 0x49
+#define SCSI_CMD_GET_EVENT_STATUS 0x4A
+#define SCSI_CMD_PAUSE_RESUME 0x4B
+#define SCSI_CMD_READ_DISC_INFORMATION 0x51
+#define SCSI_CMD_READ_TRACK_INFORMATION 0x52
+#define SCSI_CMD_RESERVE_TRACK 0x53
+#define SCSI_CMD_SEND_OPC_INFORMATION 0x54
+#define SCSI_CMD_MODE_SELECT_10 0x55
+#define SCSI_CMD_REPAIR_TRACK 0x58
+#define SCSI_CMD_MODE_SENSE_10 0x5A
+#define SCSI_CMD_CLOSE_TRACK_SESSION 0x5B
+#define SCSI_CMD_READ_BUFFER_CAPACITY 0x5C
+#define SCSI_CMD_SEND_CUE_SHEET 0x5D
+/* Group 5 Commands (CDB's here are 12-bytes) */
+#define SCSI_CMD_REPORT_LUNS 0xA0
+#define SCSI_CMD_BLANK 0xA1
+#define SCSI_CMD_SECURITY_PROTOCOL_IN 0xA2
+#define SCSI_CMD_SEND_KEY 0xA3
+#define SCSI_CMD_REPORT_KEY 0xA4
+#define SCSI_CMD_PLAY_AUDIO_12 0xA5
+#define SCSI_CMD_LOAD_UNLOAD 0xA6
+#define SCSI_CMD_SET_READ_AHEAD 0xA7
+#define SCSI_CMD_READ_12 0xA8
+#define SCSI_CMD_PLAY_TRACK_REL_12 0xA9
+#define SCSI_CMD_WRITE_12 0xAA
+#define SCSI_CMD_READ_MEDIA_SERIAL_12 0xAB
+#define SCSI_CMD_GET_PERFORMANCE 0xAC
+#define SCSI_CMD_READ_DVD_STRUCTURE 0xAD
+#define SCSI_CMD_SECURITY_PROTOCOL_OUT 0xB5
+#define SCSI_CMD_SET_STREAMING 0xB6
+#define SCSI_CMD_READ_MSF 0xB9
+#define SCSI_CMD_SET_SPEED 0xBB
+#define SCSI_CMD_MECHANISM_STATUS 0xBD
+#define SCSI_CMD_READ_CD 0xBE
+#define SCSI_CMD_SEND_DISC_STRUCTURE 0xBF
+/* Vendor-unique Commands, included for completeness */
+#define SCSI_CMD_CD_PLAYBACK_STATUS 0xC4 /* SONY unique */
+#define SCSI_CMD_PLAYBACK_CONTROL 0xC9 /* SONY unique */
+#define SCSI_CMD_READ_CDDA 0xD8 /* Vendor unique */
+#define SCSI_CMD_READ_CDXA 0xDB /* Vendor unique */
+#define SCSI_CMD_READ_ALL_SUBCODES 0xDF /* Vendor unique */
+
+/* SCSI error codes */
+#define SCSI_S_NOT_READY 0x02
+#define SCSI_S_MEDIUM_ERROR 0x03
+#define SCSI_S_ILLEGAL_REQUEST 0x05
+#define SCSI_S_UNIT_ATTENTION 0x06
+#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21
+#define SCSI_ASC_MEDIA_CHANGED 0x28
+#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A
+
+/* USB error codes */
+#define MASS_ERR_SUCCESS 0x00
+#define MASS_ERR_PHASE_ERROR 0x02
+#define MASS_ERR_UNIT_NOT_READY 0x03
+#define MASS_ERR_UNIT_BUSY 0x04
+#define MASS_ERR_STALL 0x05
+#define MASS_ERR_CMD_NOT_SUPPORTED 0x06
+#define MASS_ERR_INVALID_CSW 0x07
+#define MASS_ERR_NO_MEDIA 0x08
+#define MASS_ERR_BAD_LBA 0x09
+#define MASS_ERR_MEDIA_CHANGED 0x0A
+#define MASS_ERR_DEVICE_DISCONNECTED 0x11
+#define MASS_ERR_UNABLE_TO_RECOVER 0x12 // Reset recovery error
+#define MASS_ERR_INVALID_LUN 0x13
+#define MASS_ERR_WRITE_STALL 0x14
+#define MASS_ERR_READ_NAKS 0x15
+#define MASS_ERR_WRITE_NAKS 0x16
+#define MASS_ERR_WRITE_PROTECTED 0x17
+#define MASS_ERR_NOT_IMPLEMENTED 0xFD
+#define MASS_ERR_GENERAL_SCSI_ERROR 0xFE
+#define MASS_ERR_GENERAL_USB_ERROR 0xFF
+#define MASS_ERR_USER 0xA0 // For subclasses to define their own error codes
+
+#define MASS_TRANS_FLG_CALLBACK 0x01 // Callback is involved
+#define MASS_TRANS_FLG_NO_STALL_CHECK 0x02 // STALL condition is not checked
+#define MASS_TRANS_FLG_NO_PHASE_CHECK 0x04 // PHASE_ERROR is not checked
+
+#define MASS_MAX_ENDPOINTS 3
+
+struct Capacity {
+ uint8_t data[8];
+ //uint32_t dwBlockAddress;
+ //uint32_t dwBlockLength;
+} __attribute__((packed));
+
+struct BASICCDB {
+ uint8_t Opcode;
+
+ unsigned unused : 5;
+ unsigned LUN : 3;
+
+ uint8_t info[12];
+} __attribute__((packed));
+
+typedef BASICCDB BASICCDB_t;
+
+struct CDB6 {
+ uint8_t Opcode;
+
+ unsigned LBAMSB : 5;
+ unsigned LUN : 3;
+
+ uint8_t LBAHB;
+ uint8_t LBALB;
+ uint8_t AllocationLength;
+ uint8_t Control;
+
+public:
+
+ CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) :
+ Opcode(_Opcode), LBAMSB(BGRAB2(LBA) & 0x1F), LUN(_LUN), LBAHB(BGRAB1(LBA)), LBALB(BGRAB0(LBA)),
+ AllocationLength(_AllocationLength), Control(_Control) {
+ }
+
+ CDB6(uint8_t _Opcode, uint8_t _LUN, uint8_t _AllocationLength, uint8_t _Control) :
+ Opcode(_Opcode), LBAMSB(0), LUN(_LUN), LBAHB(0), LBALB(0),
+ AllocationLength(_AllocationLength), Control(_Control) {
+ }
+} __attribute__((packed));
+
+typedef CDB6 CDB6_t;
+
+struct CDB10 {
+ uint8_t Opcode;
+
+ unsigned Service_Action : 5;
+ unsigned LUN : 3;
+
+ uint8_t LBA_L_M_MB;
+ uint8_t LBA_L_M_LB;
+ uint8_t LBA_L_L_MB;
+ uint8_t LBA_L_L_LB;
+
+ uint8_t Misc2;
+
+ uint8_t ALC_MB;
+ uint8_t ALC_LB;
+
+ uint8_t Control;
+public:
+
+ CDB10(uint8_t _Opcode, uint8_t _LUN) :
+ Opcode(_Opcode), Service_Action(0), LUN(_LUN),
+ LBA_L_M_MB(0), LBA_L_M_LB(0), LBA_L_L_MB(0), LBA_L_L_LB(0),
+ Misc2(0), ALC_MB(0), ALC_LB(0), Control(0) {
+ }
+
+ CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) :
+ Opcode(_Opcode), Service_Action(0), LUN(_LUN),
+ LBA_L_M_MB(BGRAB3(_LBA)), LBA_L_M_LB(BGRAB2(_LBA)), LBA_L_L_MB(BGRAB1(_LBA)), LBA_L_L_LB(BGRAB0(_LBA)),
+ Misc2(0), ALC_MB(BGRAB1(xflen)), ALC_LB(BGRAB0(xflen)), Control(0) {
+ }
+} __attribute__((packed));
+
+typedef CDB10 CDB10_t;
+
+struct CDB12 {
+ uint8_t Opcode;
+
+ unsigned Service_Action : 5;
+ unsigned Misc : 3;
+
+ uint8_t LBA_L_M_LB;
+ uint8_t LBA_L_L_MB;
+ uint8_t LBA_L_L_LB;
+
+ uint8_t ALC_M_LB;
+ uint8_t ALC_L_MB;
+ uint8_t ALC_L_LB;
+ uint8_t Control;
+} __attribute__((packed));
+
+typedef CDB12 CDB12_t;
+
+struct CDB_LBA32_16 {
+ uint8_t Opcode;
+
+ unsigned Service_Action : 5;
+ unsigned Misc : 3;
+
+ uint8_t LBA_L_M_MB;
+ uint8_t LBA_L_M_LB;
+ uint8_t LBA_L_L_MB;
+ uint8_t LBA_L_L_LB;
+
+ uint8_t A_M_M_MB;
+ uint8_t A_M_M_LB;
+ uint8_t A_M_L_MB;
+ uint8_t A_M_L_LB;
+
+ uint8_t ALC_M_MB;
+ uint8_t ALC_M_LB;
+ uint8_t ALC_L_MB;
+ uint8_t ALC_L_LB;
+
+ uint8_t Misc2;
+ uint8_t Control;
+} __attribute__((packed));
+
+struct CDB_LBA64_16 {
+ uint8_t Opcode;
+ uint8_t Misc;
+
+ uint8_t LBA_M_M_MB;
+ uint8_t LBA_M_M_LB;
+ uint8_t LBA_M_L_MB;
+ uint8_t LBA_M_L_LB;
+
+ uint8_t LBA_L_M_MB;
+ uint8_t LBA_L_M_LB;
+ uint8_t LBA_L_L_MB;
+ uint8_t LBA_L_L_LB;
+
+ uint8_t ALC_M_MB;
+ uint8_t ALC_M_LB;
+ uint8_t ALC_L_MB;
+ uint8_t ALC_L_LB;
+
+ uint8_t Misc2;
+ uint8_t Control;
+} __attribute__((packed));
+
+struct InquiryResponse {
+ uint8_t DeviceType : 5;
+ uint8_t PeripheralQualifier : 3;
+
+ unsigned Reserved : 7;
+ unsigned Removable : 1;
+
+ uint8_t Version;
+
+ unsigned ResponseDataFormat : 4;
+ unsigned HISUP : 1;
+ unsigned NormACA : 1;
+ unsigned TrmTsk : 1;
+ unsigned AERC : 1;
+
+ uint8_t AdditionalLength;
+ //uint8_t Reserved3[2];
+
+ unsigned PROTECT : 1;
+ unsigned Res : 2;
+ unsigned ThreePC : 1;
+ unsigned TPGS : 2;
+ unsigned ACC : 1;
+ unsigned SCCS : 1;
+
+ unsigned ADDR16 : 1;
+ unsigned R1 : 1;
+ unsigned R2 : 1;
+ unsigned MCHNGR : 1;
+ unsigned MULTIP : 1;
+ unsigned VS : 1;
+ unsigned ENCSERV : 1;
+ unsigned BQUE : 1;
+
+ unsigned SoftReset : 1;
+ unsigned CmdQue : 1;
+ unsigned Reserved4 : 1;
+ unsigned Linked : 1;
+ unsigned Sync : 1;
+ unsigned WideBus16Bit : 1;
+ unsigned WideBus32Bit : 1;
+ unsigned RelAddr : 1;
+
+ uint8_t VendorID[8];
+ uint8_t ProductID[16];
+ uint8_t RevisionID[4];
+} __attribute__((packed));
+
+struct CommandBlockWrapperBase {
+ uint32_t dCBWSignature;
+ uint32_t dCBWTag;
+ uint32_t dCBWDataTransferLength;
+ uint8_t bmCBWFlags;
+public:
+
+ CommandBlockWrapperBase() {
+ }
+
+ CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) :
+ dCBWSignature(MASS_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) {
+ }
+} __attribute__((packed));
+
+struct CommandBlockWrapper : public CommandBlockWrapperBase {
+
+ struct {
+ uint8_t bmCBWLUN : 4;
+ uint8_t bmReserved1 : 4;
+ };
+
+ struct {
+ uint8_t bmCBWCBLength : 4;
+ uint8_t bmReserved2 : 4;
+ };
+
+ uint8_t CBWCB[16];
+
+public:
+ // All zeroed.
+
+ CommandBlockWrapper() :
+ CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) {
+ for (int i = 0; i < 16; i++) CBWCB[i] = 0;
+ }
+
+ // Generic Wrap, CDB zeroed.
+
+ CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) :
+ CommandBlockWrapperBase(tag, xflen, flgs),
+ bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) {
+ for (int i = 0; i < 16; i++) CBWCB[i] = 0;
+ // Type punning can cause optimization problems and bugs.
+ // Using reinterpret_cast to a dreinterpretifferent object is the proper way to do this.
+ //(((BASICCDB_t *) CBWCB)->LUN) = cmd;
+ BASICCDB_t *x = reinterpret_cast<BASICCDB_t *>(CBWCB);
+ x->LUN = cmd;
+ }
+
+ // Wrap for CDB of 6
+
+ CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB6_t *cdb, uint8_t dir) :
+ CommandBlockWrapperBase(tag, xflen, dir),
+ bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) {
+ memcpy(&CBWCB, cdb, 6);
+ }
+ // Wrap for CDB of 10
+
+ CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB10_t *cdb, uint8_t dir) :
+ CommandBlockWrapperBase(tag, xflen, dir),
+ bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) {
+ memcpy(&CBWCB, cdb, 10);
+ }
+} __attribute__((packed));
+
+struct CommandStatusWrapper {
+ uint32_t dCSWSignature;
+ uint32_t dCSWTag;
+ uint32_t dCSWDataResidue;
+ uint8_t bCSWStatus;
+} __attribute__((packed));
+
+struct RequestSenseResponce {
+ uint8_t bResponseCode;
+ uint8_t bSegmentNumber;
+
+ uint8_t bmSenseKey : 4;
+ uint8_t bmReserved : 1;
+ uint8_t bmILI : 1;
+ uint8_t bmEOM : 1;
+ uint8_t bmFileMark : 1;
+
+ uint8_t Information[4];
+ uint8_t bAdditionalLength;
+ uint8_t CmdSpecificInformation[4];
+ uint8_t bAdditionalSenseCode;
+ uint8_t bAdditionalSenseQualifier;
+ uint8_t bFieldReplaceableUnitCode;
+ uint8_t SenseKeySpecific[3];
+} __attribute__((packed));
+
+class BulkOnly : public USBDeviceConfig, public UsbConfigXtracter {
+protected:
+ static const uint8_t epDataInIndex; // DataIn endpoint index
+ static const uint8_t epDataOutIndex; // DataOUT endpoint index
+ static const uint8_t epInterruptInIndex; // InterruptIN endpoint index
+
+ USB *pUsb;
+ uint8_t bAddress;
+ uint8_t bConfNum; // configuration number
+ uint8_t bIface; // interface value
+ uint8_t bNumEP; // total number of EP in the configuration
+ uint32_t qNextPollTime; // next poll time
+ bool bPollEnable; // poll enable flag
+
+ EpInfo epInfo[MASS_MAX_ENDPOINTS];
+
+ uint32_t dCBWTag; // Tag
+ //uint32_t dCBWDataTransferLength; // Data Transfer Length
+ uint8_t bLastUsbError; // Last USB error
+ uint8_t bMaxLUN; // Max LUN
+ uint8_t bTheLUN; // Active LUN
+ uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors
+ uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits
+ bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes.
+ bool WriteOk[MASS_MAX_SUPPORTED_LUN];
+ void PrintEndpointDescriptor(const USB_FD_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+ // Additional Initialization Method for Subclasses
+
+ virtual uint8_t OnInit() { return 0; }
+
+public:
+ BulkOnly(USB *p);
+
+ uint8_t GetLastUsbError() { return bLastUsbError; };
+
+ uint8_t GetbMaxLUN() { return bMaxLUN; } // Max LUN
+ uint8_t GetbTheLUN() { return bTheLUN; } // Active LUN
+
+ bool WriteProtected(uint8_t lun);
+ uint8_t MediaCTL(uint8_t lun, uint8_t ctl);
+ uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf);
+ uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser *prs);
+ uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t *buf);
+ uint8_t LockMedia(uint8_t lun, uint8_t lock);
+
+ bool LUNIsGood(uint8_t lun);
+ uint32_t GetCapacity(uint8_t lun);
+ uint16_t GetSectorSize(uint8_t lun);
+
+ // USBDeviceConfig implementation
+ uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+ uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
+
+ uint8_t Release();
+ uint8_t Poll();
+
+ virtual uint8_t GetAddress() { return bAddress; }
+
+ // UsbConfigXtracter implementation
+ void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_FD_ENDPOINT_DESCRIPTOR *ep);
+
+ virtual bool DEVCLASSOK(uint8_t klass) { return klass == USB_CLASS_MASS_STORAGE; }
+
+ uint8_t SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
+ uint8_t SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
+
+private:
+ uint8_t Inquiry(uint8_t lun, uint16_t size, uint8_t *buf);
+ uint8_t TestUnitReady(uint8_t lun);
+ uint8_t RequestSense(uint8_t lun, uint16_t size, uint8_t *buf);
+ uint8_t ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t *buf);
+ uint8_t GetMaxLUN(uint8_t *max_lun);
+ uint8_t SetCurLUN(uint8_t lun);
+ void Reset();
+ uint8_t ResetRecovery();
+ uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf);
+ void ClearAllEP();
+ void CheckMedia();
+ bool CheckLUN(uint8_t lun);
+ uint8_t Page3F(uint8_t lun);
+ bool IsValidCBW(uint8_t size, uint8_t *pcbw);
+ bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw);
+
+ bool IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw);
+
+ uint8_t ClearEpHalt(uint8_t index);
+ #if MS_WANT_PARSER
+ uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf, uint8_t flags);
+ #endif
+ uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf);
+ uint8_t HandleUsbError(uint8_t error, uint8_t index);
+ uint8_t HandleSCSIError(uint8_t status);
+};
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/max3421e.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/max3421e.h
new file mode 100644
index 0000000..6cad39d
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/max3421e.h
@@ -0,0 +1,242 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include max3421e.h directly; include Usb.h instead"
+#endif
+
+/* MAX3421E register/bit names and bitmasks */
+
+/* Arduino pin definitions */
+/* pin numbers to port numbers */
+
+#define SE0 0
+#define SE1 1
+#define FSHOST 2
+#define LSHOST 3
+
+/* MAX3421E command byte format: rrrrr0wa where 'r' is register number */
+//
+// MAX3421E Registers in HOST mode.
+//
+#define rRCVFIFO 0x08 //1<<3
+#define rSNDFIFO 0x10 //2<<3
+#define rSUDFIFO 0x20 //4<<3
+#define rRCVBC 0x30 //6<<3
+#define rSNDBC 0x38 //7<<3
+
+#define rUSBIRQ 0x68 //13<<3
+/* USBIRQ Bits */
+#define bmVBUSIRQ 0x40 //b6
+#define bmNOVBUSIRQ 0x20 //b5
+#define bmOSCOKIRQ 0x01 //b0
+
+#define rUSBIEN 0x70 //14<<3
+/* USBIEN Bits */
+#define bmVBUSIE 0x40 //b6
+#define bmNOVBUSIE 0x20 //b5
+#define bmOSCOKIE 0x01 //b0
+
+#define rUSBCTL 0x78 //15<<3
+/* USBCTL Bits */
+#define bmCHIPRES 0x20 //b5
+#define bmPWRDOWN 0x10 //b4
+
+#define rCPUCTL 0x80 //16<<3
+/* CPUCTL Bits */
+#define bmPUSLEWID1 0x80 //b7
+#define bmPULSEWID0 0x40 //b6
+#define bmIE 0x01 //b0
+
+#define rPINCTL 0x88 //17<<3
+/* PINCTL Bits */
+#define bmFDUPSPI 0x10 //b4
+#define bmINTLEVEL 0x08 //b3
+#define bmPOSINT 0x04 //b2
+#define bmGPXB 0x02 //b1
+#define bmGPXA 0x01 //b0
+// GPX pin selections
+#define GPX_OPERATE 0x00
+#define GPX_VBDET 0x01
+#define GPX_BUSACT 0x02
+#define GPX_SOF 0x03
+
+#define rREVISION 0x90 //18<<3
+
+#define rIOPINS1 0xA0 //20<<3
+
+/* IOPINS1 Bits */
+#define bmGPOUT0 0x01
+#define bmGPOUT1 0x02
+#define bmGPOUT2 0x04
+#define bmGPOUT3 0x08
+#define bmGPIN0 0x10
+#define bmGPIN1 0x20
+#define bmGPIN2 0x40
+#define bmGPIN3 0x80
+
+#define rIOPINS2 0xA8 //21<<3
+/* IOPINS2 Bits */
+#define bmGPOUT4 0x01
+#define bmGPOUT5 0x02
+#define bmGPOUT6 0x04
+#define bmGPOUT7 0x08
+#define bmGPIN4 0x10
+#define bmGPIN5 0x20
+#define bmGPIN6 0x40
+#define bmGPIN7 0x80
+
+#define rGPINIRQ 0xB0 //22<<3
+/* GPINIRQ Bits */
+#define bmGPINIRQ0 0x01
+#define bmGPINIRQ1 0x02
+#define bmGPINIRQ2 0x04
+#define bmGPINIRQ3 0x08
+#define bmGPINIRQ4 0x10
+#define bmGPINIRQ5 0x20
+#define bmGPINIRQ6 0x40
+#define bmGPINIRQ7 0x80
+
+#define rGPINIEN 0xB8 //23<<3
+/* GPINIEN Bits */
+#define bmGPINIEN0 0x01
+#define bmGPINIEN1 0x02
+#define bmGPINIEN2 0x04
+#define bmGPINIEN3 0x08
+#define bmGPINIEN4 0x10
+#define bmGPINIEN5 0x20
+#define bmGPINIEN6 0x40
+#define bmGPINIEN7 0x80
+
+#define rGPINPOL 0xC0 //24<<3
+/* GPINPOL Bits */
+#define bmGPINPOL0 0x01
+#define bmGPINPOL1 0x02
+#define bmGPINPOL2 0x04
+#define bmGPINPOL3 0x08
+#define bmGPINPOL4 0x10
+#define bmGPINPOL5 0x20
+#define bmGPINPOL6 0x40
+#define bmGPINPOL7 0x80
+
+#define rHIRQ 0xC8 //25<<3
+/* HIRQ Bits */
+#define bmBUSEVENTIRQ 0x01 // indicates BUS Reset Done or BUS Resume
+#define bmRWUIRQ 0x02
+#define bmRCVDAVIRQ 0x04
+#define bmSNDBAVIRQ 0x08
+#define bmSUSDNIRQ 0x10
+#define bmCONDETIRQ 0x20
+#define bmFRAMEIRQ 0x40
+#define bmHXFRDNIRQ 0x80
+
+#define rHIEN 0xD0 //26<<3
+
+/* HIEN Bits */
+#define bmBUSEVENTIE 0x01
+#define bmRWUIE 0x02
+#define bmRCVDAVIE 0x04
+#define bmSNDBAVIE 0x08
+#define bmSUSDNIE 0x10
+#define bmCONDETIE 0x20
+#define bmFRAMEIE 0x40
+#define bmHXFRDNIE 0x80
+
+#define rMODE 0xD8 //27<<3
+
+/* MODE Bits */
+#define bmHOST 0x01
+#define bmLOWSPEED 0x02
+#define bmHUBPRE 0x04
+#define bmSOFKAENAB 0x08
+#define bmSEPIRQ 0x10
+#define bmDELAYISO 0x20
+#define bmDMPULLDN 0x40
+#define bmDPPULLDN 0x80
+
+#define rPERADDR 0xE0 //28<<3
+
+#define rHCTL 0xE8 //29<<3
+/* HCTL Bits */
+#define bmBUSRST 0x01
+#define bmFRMRST 0x02
+#define bmSAMPLEBUS 0x04
+#define bmSIGRSM 0x08
+#define bmRCVTOG0 0x10
+#define bmRCVTOG1 0x20
+#define bmSNDTOG0 0x40
+#define bmSNDTOG1 0x80
+
+#define rHXFR 0xF0 //30<<3
+
+#undef tokSETUP
+#undef tokIN
+#undef tokOUT
+#undef tokINHS
+#undef tokOUTHS
+#undef tokISOIN
+#undef tokISOOUT
+
+/* Host transfer token values for writing the HXFR register (R30) */
+/* OR this bit field with the endpoint number in bits 3:0 */
+#define tokSETUP 0x10 // HS=0, ISO=0, OUTNIN=0, SETUP=1
+#define tokIN 0x00 // HS=0, ISO=0, OUTNIN=0, SETUP=0
+#define tokOUT 0x20 // HS=0, ISO=0, OUTNIN=1, SETUP=0
+#define tokINHS 0x80 // HS=1, ISO=0, OUTNIN=0, SETUP=0
+#define tokOUTHS 0xA0 // HS=1, ISO=0, OUTNIN=1, SETUP=0
+#define tokISOIN 0x40 // HS=0, ISO=1, OUTNIN=0, SETUP=0
+#define tokISOOUT 0x60 // HS=0, ISO=1, OUTNIN=1, SETUP=0
+
+#define rHRSL 0xF8 //31<<3
+
+/* HRSL Bits */
+#define bmRCVTOGRD 0x10
+#define bmSNDTOGRD 0x20
+#define bmKSTATUS 0x40
+#define bmJSTATUS 0x80
+#define bmSE0 0x00 //SE0 - disconnect state
+#define bmSE1 0xC0 //SE1 - illegal state
+
+/* Host error result codes, the 4 LSB's in the HRSL register */
+#define hrSUCCESS 0x00
+#define hrBUSY 0x01
+#define hrBADREQ 0x02
+#define hrUNDEF 0x03
+#define hrNAK 0x04
+#define hrSTALL 0x05
+#define hrTOGERR 0x06
+#define hrWRONGPID 0x07
+#define hrBADBC 0x08
+#define hrPIDERR 0x09
+#define hrPKTERR 0x0A
+#define hrCRCERR 0x0B
+#define hrKERR 0x0C
+#define hrJERR 0x0D
+#define hrTIMEOUT 0x0E
+#define hrBABBLE 0x0F
+
+#define MODE_FS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB)
+#define MODE_LS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB)
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/message.cpp b/Marlin/src/sd/usb_flashdrive/lib-uhs2/message.cpp
new file mode 100644
index 0000000..dcc3090
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/message.cpp
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT) && DISABLED(USE_UHS3_USB)
+
+#include "Usb.h"
+
+// 0x80 is the default (i.e. trace) to turn off set this global to something lower.
+// this allows for 126 other debugging levels.
+// TO-DO: Allow assignment to a different serial port by software
+int UsbDEBUGlvl = 0x80;
+
+void E_Notifyc(char c, int lvl) {
+ if (UsbDEBUGlvl < lvl) return;
+ USB_HOST_SERIAL.print(c
+ #if !defined(ARDUINO) && !defined(ARDUINO_ARCH_LPC176X)
+ , BYTE
+ #endif
+ );
+ //USB_HOST_SERIAL.flush();
+}
+
+void E_Notify(char const * msg, int lvl) {
+ if (UsbDEBUGlvl < lvl) return;
+ if (!msg) return;
+ while (const char c = pgm_read_byte(msg++)) E_Notifyc(c, lvl);
+}
+
+void E_NotifyStr(char const * msg, int lvl) {
+ if (UsbDEBUGlvl < lvl) return;
+ if (!msg) return;
+ while (const char c = *msg++) E_Notifyc(c, lvl);
+}
+
+void E_Notify(uint8_t b, int lvl) {
+ if (UsbDEBUGlvl < lvl) return;
+ USB_HOST_SERIAL.print(b
+ #if !defined(ARDUINO) || ARDUINO < 100
+ , DEC
+ #endif
+ );
+ //USB_HOST_SERIAL.flush();
+}
+
+void E_Notify(double d, int lvl) {
+ if (UsbDEBUGlvl < lvl) return;
+ USB_HOST_SERIAL.print(d);
+ //USB_HOST_SERIAL.flush();
+}
+
+#ifdef DEBUG_USB_HOST
+
+ void NotifyFailGetDevDescr() {
+ Notify(PSTR("\r\ngetDevDescr "), 0x80);
+ }
+
+ void NotifyFailSetDevTblEntry() {
+ Notify(PSTR("\r\nsetDevTblEn "), 0x80);
+ }
+
+ void NotifyFailGetConfDescr() {
+ Notify(PSTR("\r\ngetConf "), 0x80);
+ }
+
+ void NotifyFailSetConfDescr() {
+ Notify(PSTR("\r\nsetConf "), 0x80);
+ }
+
+ void NotifyFailGetDevDescr(uint8_t reason) {
+ NotifyFailGetDevDescr();
+ NotifyFail(reason);
+ }
+
+ void NotifyFailSetDevTblEntry(uint8_t reason) {
+ NotifyFailSetDevTblEntry();
+ NotifyFail(reason);
+
+ }
+
+ void NotifyFailGetConfDescr(uint8_t reason) {
+ NotifyFailGetConfDescr();
+ NotifyFail(reason);
+ }
+
+ void NotifyFailSetConfDescr(uint8_t reason) {
+ NotifyFailSetConfDescr();
+ NotifyFail(reason);
+ }
+
+ void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID) {
+ Notify(PSTR("\r\nUnknown Device Connected - VID: "), 0x80);
+ D_PrintHex<uint16_t > (VID, 0x80);
+ Notify(PSTR(" PID: "), 0x80);
+ D_PrintHex<uint16_t > (PID, 0x80);
+ }
+
+ void NotifyFail(uint8_t rcode) {
+ D_PrintHex<uint8_t > (rcode, 0x80);
+ Notify(PSTR("\r\n"), 0x80);
+ }
+
+#endif // DEBUG_USB_HOST
+
+#endif // USB_FLASH_DRIVE_SUPPORT
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/message.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/message.h
new file mode 100644
index 0000000..12195dc
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/message.h
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include message.h directly; include Usb.h instead"
+#endif
+
+extern int UsbDEBUGlvl;
+
+void E_Notify(char const * msg, int lvl);
+void E_Notify(uint8_t b, int lvl);
+void E_NotifyStr(char const * msg, int lvl);
+void E_Notifyc(char c, int lvl);
+
+#ifdef DEBUG_USB_HOST
+ #define Notify E_Notify
+ #define NotifyStr E_NotifyStr
+ #define Notifyc E_Notifyc
+ void NotifyFailGetDevDescr(uint8_t reason);
+ void NotifyFailSetDevTblEntry(uint8_t reason);
+ void NotifyFailGetConfDescr(uint8_t reason);
+ void NotifyFailSetConfDescr(uint8_t reason);
+ void NotifyFailGetDevDescr();
+ void NotifyFailSetDevTblEntry();
+ void NotifyFailGetConfDescr();
+ void NotifyFailSetConfDescr();
+ void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID);
+ void NotifyFail(uint8_t rcode);
+#else
+ #define Notify(...) ((void)0)
+ #define NotifyStr(...) ((void)0)
+ #define Notifyc(...) ((void)0)
+ #define NotifyFailGetDevDescr(...) ((void)0)
+ #define NotifyFailSetDevTblEntry(...) ((void)0)
+ #define NotifyFailGetConfDescr(...) ((void)0)
+ #define NotifyFailGetDevDescr(...) ((void)0)
+ #define NotifyFailSetDevTblEntry(...) ((void)0)
+ #define NotifyFailGetConfDescr(...) ((void)0)
+ #define NotifyFailSetConfDescr(...) ((void)0)
+ #define NotifyFailUnknownDevice(...) ((void)0)
+ #define NotifyFail(...) ((void)0)
+#endif
+
+template <class ERROR_TYPE>
+void ErrorMessage(uint8_t level, char const * msg, ERROR_TYPE rcode = 0) {
+ #ifdef DEBUG_USB_HOST
+ Notify(msg, level);
+ Notify(PSTR(": "), level);
+ D_PrintHex<ERROR_TYPE > (rcode, level);
+ Notify(PSTR("\r\n"), level);
+ #endif
+}
+
+template <class ERROR_TYPE>
+void ErrorMessage(char const * msg __attribute__((unused)), ERROR_TYPE rcode __attribute__((unused)) = 0) {
+ #ifdef DEBUG_USB_HOST
+ Notify(msg, 0x80);
+ Notify(PSTR(": "), 0x80);
+ D_PrintHex<ERROR_TYPE > (rcode, 0x80);
+ Notify(PSTR("\r\n"), 0x80);
+ #endif
+}
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.cpp b/Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.cpp
new file mode 100644
index 0000000..5d25576
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.cpp
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT) && DISABLED(USE_UHS3_USB)
+
+#include "Usb.h"
+
+bool MultiByteValueParser::Parse(uint8_t **pp, uint16_t *pcntdn) {
+ if (!pBuf) {
+ Notify(PSTR("Buffer pointer is NULL!\r\n"), 0x80);
+ return false;
+ }
+ for (; countDown && (*pcntdn); countDown--, (*pcntdn)--, (*pp)++)
+ pBuf[valueSize - countDown] = (**pp);
+
+ if (countDown) return false;
+
+ countDown = valueSize;
+ return true;
+}
+
+bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me) {
+ switch (nStage) {
+ case 0:
+ pBuf->valueSize = lenSize;
+ theParser.Initialize(pBuf);
+ nStage = 1;
+
+ case 1:
+ if (!theParser.Parse(pp, pcntdn)) return false;
+
+ arLen = 0;
+ arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue));
+ arLenCntdn = arLen;
+ nStage = 2;
+
+ case 2:
+ pBuf->valueSize = valSize;
+ theParser.Initialize(pBuf);
+ nStage = 3;
+
+ case 3:
+ for (; arLenCntdn; arLenCntdn--) {
+ if (!theParser.Parse(pp, pcntdn)) return false;
+ if (pf) pf(pBuf, (arLen - arLenCntdn), me);
+ }
+
+ nStage = 0;
+ }
+ return true;
+}
+
+#endif // USB_FLASH_DRIVE_SUPPORT
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.h
new file mode 100644
index 0000000..403766d
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/parsetools.h
@@ -0,0 +1,145 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include parsetools.h directly; include Usb.h instead"
+#endif
+
+struct MultiValueBuffer {
+ uint8_t valueSize;
+ void *pValue;
+} __attribute__((packed));
+
+class MultiByteValueParser {
+ uint8_t * pBuf;
+ uint8_t countDown;
+ uint8_t valueSize;
+
+public:
+
+ MultiByteValueParser() : pBuf(nullptr), countDown(0), valueSize(0) {
+ };
+
+ const uint8_t* GetBuffer() { return pBuf; }
+
+ void Initialize(MultiValueBuffer * const pbuf) {
+ pBuf = (uint8_t*)pbuf->pValue;
+ countDown = valueSize = pbuf->valueSize;
+ }
+
+ bool Parse(uint8_t **pp, uint16_t *pcntdn);
+};
+
+class ByteSkipper {
+ uint8_t *pBuf;
+ uint8_t nStage;
+ uint16_t countDown;
+
+public:
+
+ ByteSkipper() : pBuf(nullptr), nStage(0), countDown(0) {
+ }
+
+ void Initialize(MultiValueBuffer *pbuf) {
+ pBuf = (uint8_t*)pbuf->pValue;
+ countDown = 0;
+ }
+
+ bool Skip(uint8_t **pp, uint16_t *pcntdn, uint16_t bytes_to_skip) {
+ switch (nStage) {
+ case 0:
+ countDown = bytes_to_skip;
+ nStage++;
+ case 1:
+ for (; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);
+
+ if (!countDown)
+ nStage = 0;
+ }
+ return (!countDown);
+ }
+};
+
+// Pointer to a callback function triggered for each element of PTP array when used with PTPArrayParser
+typedef void (*PTP_ARRAY_EL_FUNC)(const MultiValueBuffer * const p, uint32_t count, const void *me);
+
+class PTPListParser {
+public:
+
+ enum ParseMode {
+ modeArray, modeRange/*, modeEnum*/
+ };
+
+private:
+ uint8_t nStage;
+ uint8_t enStage;
+
+ uint32_t arLen;
+ uint32_t arLenCntdn;
+
+ uint8_t lenSize; // size of the array length field in bytes
+ uint8_t valSize; // size of the array element in bytes
+
+ MultiValueBuffer *pBuf;
+
+ // The only parser for both size and array element parsing
+ MultiByteValueParser theParser;
+
+ uint8_t /*ParseMode*/ prsMode;
+
+public:
+
+ PTPListParser() :
+ nStage(0),
+ enStage(0),
+ arLen(0),
+ arLenCntdn(0),
+ lenSize(0),
+ valSize(0),
+ pBuf(nullptr),
+ prsMode(modeArray) {}
+ ;
+
+ void Initialize(const uint8_t len_size, const uint8_t val_size, MultiValueBuffer * const p, const uint8_t mode = modeArray) {
+ pBuf = p;
+ lenSize = len_size;
+ valSize = val_size;
+ prsMode = mode;
+
+ if (prsMode == modeRange) {
+ arLenCntdn = arLen = 3;
+ nStage = 2;
+ }
+ else {
+ arLenCntdn = arLen = 0;
+ nStage = 0;
+ }
+ enStage = 0;
+ theParser.Initialize(p);
+ }
+
+ bool Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me = nullptr);
+};
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/printhex.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/printhex.h
new file mode 100644
index 0000000..319cd9c
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/printhex.h
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+#pragma once
+
+#ifndef _usb_h_
+ #error "Never include printhex.h directly; include Usb.h instead"
+#endif
+
+void E_Notifyc(char c, int lvl);
+
+template <class T>
+void PrintHex(T val, int lvl) {
+ int num_nibbles = sizeof (T) * 2;
+ do {
+ char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0F);
+ if (v > 57) v += 7;
+ E_Notifyc(v, lvl);
+ } while (--num_nibbles);
+}
+
+template <class T>
+void PrintBin(T val, int lvl) {
+ for (T mask = (((T)1) << ((sizeof (T) << 3) - 1)); mask; mask >>= 1)
+ E_Notifyc(val & mask ? '1' : '0', lvl);
+}
+
+template <class T>
+void SerialPrintHex(T val) {
+ int num_nibbles = sizeof (T) * 2;
+ do {
+ char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0F);
+ if (v > 57) v += 7;
+ USB_HOST_SERIAL.print(v);
+ } while (--num_nibbles);
+}
+
+template <class T>
+void PrintHex2(Print *prn, T val) {
+ T mask = (((T)1) << (((sizeof (T) << 1) - 1) << 2));
+ while (mask > 1) {
+ if (val < mask) prn->print("0");
+ mask >>= 4;
+ }
+ prn->print((T)val, HEX);
+}
+
+template <class T> void D_PrintHex(T val __attribute__((unused)), int lvl __attribute__((unused))) {
+ #ifdef DEBUG_USB_HOST
+ PrintHex<T > (val, lvl);
+ #endif
+}
+
+template <class T>
+void D_PrintBin(T val, int lvl) {
+ #ifdef DEBUG_USB_HOST
+ PrintBin<T > (val, lvl);
+ #endif
+}
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/settings.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/settings.h
new file mode 100644
index 0000000..2de0d46
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/settings.h
@@ -0,0 +1,236 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#pragma once
+
+#include "../../../inc/MarlinConfig.h"
+
+#include "macros.h"
+
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
+ ////////////////////////////////////////////////////////////////////////////////
+ /* Added by Bill Greiman to speed up mass storage initialization with USB
+ * flash drives and simple USB hard drives.
+ * Disable this by defining DELAY(x) to be delay(x).
+ */
+ #define delay(x) if ((x) < 200) safe_delay(x)
+ /* Almost all USB flash drives and simple USB hard drives fail the write
+ * protect test and add 20 - 30 seconds to USB init. Set SKIP_WRITE_PROTECT
+ * to nonzero to skip the test and assume the drive is writable.
+ */
+ #define SKIP_WRITE_PROTECT 1
+ /* Since Marlin only cares about USB flash drives, we only need one LUN. */
+ #define MASS_MAX_SUPPORTED_LUN 1
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// SPI Configuration
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef USB_SPI
+ #define USB_SPI SPI
+ //#define USB_SPI SPI1
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DEBUGGING
+////////////////////////////////////////////////////////////////////////////////
+
+/* Set this to 1 to activate serial debugging */
+#define ENABLE_UHS_DEBUGGING 0
+
+/* This can be used to select which serial port to use for debugging if
+ * multiple serial ports are available.
+ * For example Serial3.
+ */
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
+ #define USB_HOST_SERIAL MYSERIAL0
+#endif
+
+#ifndef USB_HOST_SERIAL
+ #define USB_HOST_SERIAL Serial
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Manual board activation
+////////////////////////////////////////////////////////////////////////////////
+
+/* Set this to 1 if you are using an Arduino Mega ADK board with MAX3421e built-in */
+#define USE_UHS_MEGA_ADK 0 // If you are using Arduino 1.5.5 or newer there is no need to do this manually
+
+/* Set this to 1 if you are using a Black Widdow */
+#define USE_UHS_BLACK_WIDDOW 0
+
+/* Set this to a one to use the xmem2 lock. This is needed for multitasking and threading */
+#define USE_XMEM_SPI_LOCK 0
+
+////////////////////////////////////////////////////////////////////////////////
+// Wii IR camera
+////////////////////////////////////////////////////////////////////////////////
+
+/* Set this to 1 to activate code for the Wii IR camera */
+#define ENABLE_WII_IR_CAMERA 0
+
+////////////////////////////////////////////////////////////////////////////////
+// MASS STORAGE
+////////////////////////////////////////////////////////////////////////////////
+// ******* IMPORTANT *******
+// Set this to 1 to support single LUN devices, and save RAM. -- I.E. thumb drives.
+// Each LUN needs ~13 bytes to be able to track the state of each unit.
+#ifndef MASS_MAX_SUPPORTED_LUN
+ #define MASS_MAX_SUPPORTED_LUN 8
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Set to 1 to use the faster spi4teensy3 driver.
+////////////////////////////////////////////////////////////////////////////////
+#ifndef USE_SPI4TEENSY3
+ #define USE_SPI4TEENSY3 1
+#endif
+
+// Disabled on the Teensy LC, as it is incompatible for now
+#ifdef __MKL26Z64__
+ #undef USE_SPI4TEENSY3
+ #define USE_SPI4TEENSY3 0
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// AUTOMATIC Settings
+////////////////////////////////////////////////////////////////////////////////
+
+// No user serviceable parts below this line.
+// DO NOT change anything below here unless you are a developer!
+
+//#include "version_helper.h"
+
+#if defined(__GNUC__) && defined(__AVR__)
+ #ifndef GCC_VERSION
+ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+ #endif
+ #if GCC_VERSION < 40602 // Test for GCC < 4.6.2
+ #ifdef PROGMEM
+ #undef PROGMEM
+ #define PROGMEM __attribute__((section(".progmem.data"))) // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734#c4
+ #ifdef PSTR
+ #undef PSTR
+ #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) // Copied from pgmspace.h in avr-libc source
+ #endif
+ #endif
+ #endif
+#endif
+
+#if !defined(DEBUG_USB_HOST) && ENABLE_UHS_DEBUGGING
+ #define DEBUG_USB_HOST
+#endif
+
+#if !defined(WIICAMERA) && ENABLE_WII_IR_CAMERA
+ #define WIICAMERA
+#endif
+
+// To use some other locking (e.g. freertos),
+// define XMEM_ACQUIRE_SPI and XMEM_RELEASE_SPI to point to your lock and unlock.
+// NOTE: NO argument is passed. You have to do this within your routine for
+// whatever you are using to lock and unlock.
+#ifndef XMEM_ACQUIRE_SPI
+ #if USE_XMEM_SPI_LOCK || defined(USE_MULTIPLE_APP_API)
+ #include <xmem.h>
+ #else
+ #define XMEM_ACQUIRE_SPI() (void(0))
+ #define XMEM_RELEASE_SPI() (void(0))
+ #endif
+#endif
+
+#if !defined(EXT_RAM) && defined(EXT_RAM_STACK) || defined(EXT_RAM_HEAP)
+ #include <xmem.h>
+#else
+ #define EXT_RAM 0
+#endif
+
+#if defined(CORE_TEENSY) && defined(KINETISK)
+ #define USING_SPI4TEENSY3 USE_SPI4TEENSY3
+#else
+ #define USING_SPI4TEENSY3 0
+#endif
+#if ((defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(__ARDUINO_X86__) || ARDUINO >= 10600) && !USING_SPI4TEENSY3
+ #include <SPI.h> // Use the Arduino SPI library for the Arduino Due, Intel Galileo 1 & 2, Intel Edison or if the SPI library with transaction is available
+#endif
+#ifdef RBL_NRF51822
+ #include <nrf_gpio.h>
+ #include <SPI_Master.h>
+ #define SPI SPI_Master
+ #define MFK_CASTUINT8T (uint8_t) // RBLs return type for sizeof needs casting to uint8_t
+#endif
+#if defined(__PIC32MX__) || defined(__PIC32MZ__)
+ #include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library
+#endif
+
+#if defined(ESP8266) || defined(ESP32)
+ #define MFK_CASTUINT8T (uint8_t) // ESP return type for sizeof needs casting to uint8_t
+#endif
+
+#ifdef STM32F4
+ #include "stm32f4xx_hal.h"
+ extern SPI_HandleTypeDef SPI_Handle; // Needed to be declared in your main.cpp
+#endif
+
+// Fix defines on Arduino Due
+#ifdef ARDUINO_SAM_DUE
+ #ifdef tokSETUP
+ #undef tokSETUP
+ #endif
+ #ifdef tokIN
+ #undef tokIN
+ #endif
+ #ifdef tokOUT
+ #undef tokOUT
+ #endif
+ #ifdef tokINHS
+ #undef tokINHS
+ #endif
+ #ifdef tokOUTHS
+ #undef tokOUTHS
+ #endif
+#endif
+
+// Set defaults
+#ifndef MFK_CASTUINT8T
+ #define MFK_CASTUINT8T
+#endif
+
+// Workaround issue: https://github.com/esp8266/Arduino/issues/2078
+#ifdef ESP8266
+ #undef PROGMEM
+ #define PROGMEM
+#undef PSTR
+ #define PSTR(s) (s)
+#undef pgm_read_byte
+ #define pgm_read_byte(addr) (*reinterpret_cast<const uint8_t*>(addr))
+ #undef pgm_read_word
+ #define pgm_read_word(addr) (*reinterpret_cast<const uint16_t*>(addr))
+#endif
+
+#ifdef ARDUINO_ESP8266_WIFIO
+ #error "This board is currently not supported"
+#endif
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/usb_ch9.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usb_ch9.h
new file mode 100644
index 0000000..99c628f
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usb_ch9.h
@@ -0,0 +1,170 @@
+/**
+ * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information
+ * -------------------
+ *
+ * Circuits At Home, LTD
+ * Web : https://www.circuitsathome.com
+ * e-mail : support@circuitsathome.com
+ */
+
+#ifndef _usb_h_
+ #error "Never include usb_ch9.h directly; include Usb.h instead"
+#endif
+
+/* USB chapter 9 structures */
+
+/* Misc.USB constants */
+#define DEV_DESCR_LEN 18 //device descriptor length
+#define CONF_DESCR_LEN 9 //configuration descriptor length
+#define INTR_DESCR_LEN 9 //interface descriptor length
+#define EP_DESCR_LEN 7 //endpoint descriptor length
+
+/* Standard Device Requests */
+
+#define USB_REQUEST_GET_STATUS 0 // Standard Device Request - GET STATUS
+#define USB_REQUEST_CLEAR_FEATURE 1 // Standard Device Request - CLEAR FEATURE
+#define USB_REQUEST_SET_FEATURE 3 // Standard Device Request - SET FEATURE
+#define USB_REQUEST_SET_ADDRESS 5 // Standard Device Request - SET ADDRESS
+#define USB_REQUEST_GET_DESCRIPTOR 6 // Standard Device Request - GET DESCRIPTOR
+#define USB_REQUEST_SET_DESCRIPTOR 7 // Standard Device Request - SET DESCRIPTOR
+#define USB_REQUEST_GET_CONFIGURATION 8 // Standard Device Request - GET CONFIGURATION
+#define USB_REQUEST_SET_CONFIGURATION 9 // Standard Device Request - SET CONFIGURATION
+#define USB_REQUEST_GET_INTERFACE 10 // Standard Device Request - GET INTERFACE
+#define USB_REQUEST_SET_INTERFACE 11 // Standard Device Request - SET INTERFACE
+#define USB_REQUEST_SYNCH_FRAME 12 // Standard Device Request - SYNCH FRAME
+
+#define USB_FEATURE_ENDPOINT_HALT 0 // CLEAR/SET FEATURE - Endpoint Halt
+#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // CLEAR/SET FEATURE - Device remote wake-up
+#define USB_FEATURE_TEST_MODE 2 // CLEAR/SET FEATURE - Test mode
+
+/* Setup Data Constants */
+
+#define USB_SETUP_HOST_TO_DEVICE 0x00 // Device Request bmRequestType transfer direction - host to device transfer
+#define USB_SETUP_DEVICE_TO_HOST 0x80 // Device Request bmRequestType transfer direction - device to host transfer
+#define USB_SETUP_TYPE_STANDARD 0x00 // Device Request bmRequestType type - standard
+#define USB_SETUP_TYPE_CLASS 0x20 // Device Request bmRequestType type - class
+#define USB_SETUP_TYPE_VENDOR 0x40 // Device Request bmRequestType type - vendor
+#define USB_SETUP_RECIPIENT_DEVICE 0x00 // Device Request bmRequestType recipient - device
+#define USB_SETUP_RECIPIENT_INTERFACE 0x01 // Device Request bmRequestType recipient - interface
+#define USB_SETUP_RECIPIENT_ENDPOINT 0x02 // Device Request bmRequestType recipient - endpoint
+#define USB_SETUP_RECIPIENT_OTHER 0x03 // Device Request bmRequestType recipient - other
+
+/* USB descriptors */
+
+#define USB_DESCRIPTOR_DEVICE 0x01 // bDescriptorType for a Device Descriptor.
+#define USB_DESCRIPTOR_CONFIGURATION 0x02 // bDescriptorType for a Configuration Descriptor.
+#define USB_DESCRIPTOR_STRING 0x03 // bDescriptorType for a String Descriptor.
+#define USB_DESCRIPTOR_INTERFACE 0x04 // bDescriptorType for an Interface Descriptor.
+#define USB_DESCRIPTOR_ENDPOINT 0x05 // bDescriptorType for an Endpoint Descriptor.
+#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06 // bDescriptorType for a Device Qualifier.
+#define USB_DESCRIPTOR_OTHER_SPEED 0x07 // bDescriptorType for a Other Speed Configuration.
+#define USB_DESCRIPTOR_INTERFACE_POWER 0x08 // bDescriptorType for Interface Power.
+#define USB_DESCRIPTOR_OTG 0x09 // bDescriptorType for an OTG Descriptor.
+
+#define HID_DESCRIPTOR_HID 0x21
+
+
+/* OTG SET FEATURE Constants */
+#define OTG_FEATURE_B_HNP_ENABLE 3 // SET FEATURE OTG - Enable B device to perform HNP
+#define OTG_FEATURE_A_HNP_SUPPORT 4 // SET FEATURE OTG - A device supports HNP
+#define OTG_FEATURE_A_ALT_HNP_SUPPORT 5 // SET FEATURE OTG - Another port on the A device supports HNP
+
+/* USB Endpoint Transfer Types */
+#define USB_TRANSFER_TYPE_CONTROL 0x00 // Endpoint is a control endpoint.
+#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x01 // Endpoint is an isochronous endpoint.
+#define USB_TRANSFER_TYPE_BULK 0x02 // Endpoint is a bulk endpoint.
+#define USB_TRANSFER_TYPE_INTERRUPT 0x03 // Endpoint is an interrupt endpoint.
+#define bmUSB_TRANSFER_TYPE 0x03 // bit mask to separate transfer type from ISO attributes
+
+
+/* Standard Feature Selectors for CLEAR_FEATURE Requests */
+#define USB_FEATURE_ENDPOINT_STALL 0 // Endpoint recipient
+#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient
+#define USB_FEATURE_TEST_MODE 2 // Device recipient
+
+/* descriptor data structures */
+
+/* Device descriptor structure */
+typedef struct {
+ uint8_t bLength; // Length of this descriptor.
+ uint8_t bDescriptorType; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE).
+ uint16_t bcdUSB; // USB Spec Release Number (BCD).
+ uint8_t bDeviceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific.
+ uint8_t bDeviceSubClass; // Subclass code (assigned by the USB-IF).
+ uint8_t bDeviceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.
+ uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0.
+ uint16_t idVendor; // Vendor ID (assigned by the USB-IF).
+ uint16_t idProduct; // Product ID (assigned by the manufacturer).
+ uint16_t bcdDevice; // Device release number (BCD).
+ uint8_t iManufacturer; // Index of String Descriptor describing the manufacturer.
+ uint8_t iProduct; // Index of String Descriptor describing the product.
+ uint8_t iSerialNumber; // Index of String Descriptor with the device's serial number.
+ uint8_t bNumConfigurations; // Number of possible configurations.
+} __attribute__((packed)) USB_FD_DEVICE_DESCRIPTOR;
+
+/* Configuration descriptor structure */
+typedef struct {
+ uint8_t bLength; // Length of this descriptor.
+ uint8_t bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION).
+ uint16_t wTotalLength; // Total length of all descriptors for this configuration.
+ uint8_t bNumInterfaces; // Number of interfaces in this configuration.
+ uint8_t bConfigurationValue; // Value of this configuration (1 based).
+ uint8_t iConfiguration; // Index of String Descriptor describing the configuration.
+ uint8_t bmAttributes; // Configuration characteristics.
+ uint8_t bMaxPower; // Maximum power consumed by this configuration.
+} __attribute__((packed)) USB_FD_CONFIGURATION_DESCRIPTOR;
+
+/* Interface descriptor structure */
+typedef struct {
+ uint8_t bLength; // Length of this descriptor.
+ uint8_t bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE).
+ uint8_t bInterfaceNumber; // Number of this interface (0 based).
+ uint8_t bAlternateSetting; // Value of this alternate interface setting.
+ uint8_t bNumEndpoints; // Number of endpoints in this interface.
+ uint8_t bInterfaceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific.
+ uint8_t bInterfaceSubClass; // Subclass code (assigned by the USB-IF).
+ uint8_t bInterfaceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.
+ uint8_t iInterface; // Index of String Descriptor describing the interface.
+} __attribute__((packed)) USB_FD_INTERFACE_DESCRIPTOR;
+
+/* Endpoint descriptor structure */
+typedef struct {
+ uint8_t bLength; // Length of this descriptor.
+ uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
+ uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).
+ uint8_t bmAttributes; // Endpoint transfer type.
+ uint16_t wMaxPacketSize; // Maximum packet size.
+ uint8_t bInterval; // Polling interval in frames.
+} __attribute__((packed)) USB_FD_ENDPOINT_DESCRIPTOR;
+
+/* HID descriptor */
+typedef struct {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdHID; // HID class specification release
+ uint8_t bCountryCode;
+ uint8_t bNumDescriptors; // Number of additional class specific descriptors
+ uint8_t bDescrType; // Type of class descriptor
+ uint16_t wDescriptorLength; // Total size of the Report descriptor
+} __attribute__((packed)) USB_HID_DESCRIPTOR;
+
+typedef struct {
+ uint8_t bDescrType; // Type of class descriptor
+ uint16_t wDescriptorLength; // Total size of the Report descriptor
+} __attribute__((packed)) HID_CLASS_DESCRIPTOR_LEN_AND_TYPE;
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp
new file mode 100644
index 0000000..190a0f1
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp
@@ -0,0 +1,207 @@
+/****************
+ * usb_host.cpp *
+ ****************/
+
+/****************************************************************************
+ * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
+ * *
+ * This program 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. *
+ * *
+ * This program 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. *
+ * *
+ * To view a copy of the GNU General Public License, go to the following *
+ * location: <https://www.gnu.org/licenses/>. *
+ ****************************************************************************/
+
+/* What follows is a modified version of the MAX3421e originally defined in
+ * lib/usbhost.c". This has been rewritten to use SPI routines from the
+ * Marlin HAL */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(USB_FLASH_DRIVE_SUPPORT) && DISABLED(USE_UHS3_USB)
+
+#include "Usb.h"
+#include "usbhost.h"
+
+uint8_t MAX3421e::vbusState = 0;
+
+// constructor
+void MAX3421e::cs() {
+ WRITE(USB_CS_PIN,0);
+}
+
+void MAX3421e::ncs() {
+ WRITE(USB_CS_PIN,1);
+}
+
+// write single byte into MAX3421 register
+void MAX3421e::regWr(uint8_t reg, uint8_t data) {
+ cs();
+ spiSend(reg | 0x02);
+ spiSend(data);
+ ncs();
+};
+
+// multiple-byte write
+// return a pointer to memory position after last written
+uint8_t* MAX3421e::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {
+ cs();
+ spiSend(reg | 0x02);
+ while (nbytes--) spiSend(*data_p++);
+ ncs();
+ return data_p;
+}
+
+// GPIO write
+// GPIO byte is split between 2 registers, so two writes are needed to write one byte
+
+// GPOUT bits are in the low nybble. 0-3 in IOPINS1, 4-7 in IOPINS2
+void MAX3421e::gpioWr(uint8_t data) {
+ regWr(rIOPINS1, data);
+ regWr(rIOPINS2, data >> 4);
+}
+
+// single host register read
+uint8_t MAX3421e::regRd(uint8_t reg) {
+ cs();
+ spiSend(reg);
+ uint8_t rv = spiRec();
+ ncs();
+ return rv;
+}
+// multiple-byte register read
+
+// return a pointer to a memory position after last read
+uint8_t* MAX3421e::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {
+ cs();
+ spiSend(reg);
+ while (nbytes--) *data_p++ = spiRec();
+ ncs();
+ return data_p;
+}
+// GPIO read. See gpioWr for explanation
+
+// GPIN pins are in high nybbles of IOPINS1, IOPINS2
+uint8_t MAX3421e::gpioRd() {
+ return (regRd(rIOPINS2) & 0xF0) | // pins 4-7, clean lower nybble
+ (regRd(rIOPINS1) >> 4); // shift low bits and OR with upper from previous operation.
+}
+
+// reset MAX3421e. Returns false if PLL failed to stabilize 1 second after reset
+bool MAX3421e::reset() {
+ regWr(rUSBCTL, bmCHIPRES);
+ regWr(rUSBCTL, 0x00);
+ for (uint8_t i = 100; i--;) {
+ if (regRd(rUSBIRQ) & bmOSCOKIRQ) return true;
+ delay(10);
+ }
+ return false;
+}
+
+// initialize MAX3421e. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not
+bool MAX3421e::start() {
+ // Initialize pins and SPI bus
+
+ SET_OUTPUT(USB_CS_PIN);
+ SET_INPUT_PULLUP(USB_INTR_PIN);
+ ncs();
+ spiBegin();
+
+ spiInit(SD_SPI_SPEED);
+
+ // MAX3421e - full-duplex, level interrupt, vbus off.
+ regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET));
+
+ const uint8_t revision = regRd(rREVISION);
+ if (revision == 0x00 || revision == 0xFF) {
+ SERIAL_ECHOLNPAIR("Revision register appears incorrect on MAX3421e initialization. Got ", revision);
+ return false;
+ }
+
+ if (!reset()) {
+ SERIAL_ECHOLNPGM("OSCOKIRQ hasn't asserted in time");
+ return false;
+ }
+
+ // Delay a minimum of 1 second to ensure any capacitors are drained.
+ // 1 second is required to make sure we do not smoke a Microdrive!
+
+ delay(1000);
+
+ regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
+ regWr(rHIEN, bmCONDETIE | bmFRAMEIE); // connection detection
+
+ // check if device is connected
+ regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
+ while (!(regRd(rHCTL) & bmSAMPLEBUS)) delay(10); // wait for sample operation to finish
+
+ busprobe(); // check if anything is connected
+
+ regWr(rHIRQ, bmCONDETIRQ); // clear connection detect interrupt
+ regWr(rCPUCTL, 0x01); // enable interrupt pin
+
+ // GPX pin on. This is done here so that busprobe will fail if we have a switch connected.
+ regWr(rPINCTL, bmFDUPSPI | bmINTLEVEL);
+
+ return true;
+}
+
+// Probe bus to determine device presence and speed. Switch host to this speed.
+void MAX3421e::busprobe() {
+ // Switch on just the J & K bits
+ switch (regRd(rHRSL) & (bmJSTATUS | bmKSTATUS)) {
+ case bmJSTATUS:
+ if ((regRd(rMODE) & bmLOWSPEED) == 0) {
+ regWr(rMODE, MODE_FS_HOST); // start full-speed host
+ vbusState = FSHOST;
+ }
+ else {
+ regWr(rMODE, MODE_LS_HOST); // start low-speed host
+ vbusState = LSHOST;
+ }
+ break;
+ case bmKSTATUS:
+ if ((regRd(rMODE) & bmLOWSPEED) == 0) {
+ regWr(rMODE, MODE_LS_HOST); // start low-speed host
+ vbusState = LSHOST;
+ }
+ else {
+ regWr(rMODE, MODE_FS_HOST); // start full-speed host
+ vbusState = FSHOST;
+ }
+ break;
+ case bmSE1: // illegal state
+ vbusState = SE1;
+ break;
+ case bmSE0: // disconnected state
+ regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ);
+ vbusState = SE0;
+ break;
+ }
+}
+
+// MAX3421 state change task and interrupt handler
+uint8_t MAX3421e::Task() {
+ return READ(USB_INTR_PIN) ? 0 : IntHandler();
+}
+
+uint8_t MAX3421e::IntHandler() {
+ uint8_t HIRQ = regRd(rHIRQ), // determine interrupt source
+ HIRQ_sendback = 0x00;
+ if (HIRQ & bmCONDETIRQ) {
+ busprobe();
+ HIRQ_sendback |= bmCONDETIRQ;
+ }
+ // End HIRQ interrupts handling, clear serviced IRQs
+ regWr(rHIRQ, HIRQ_sendback);
+ return HIRQ_sendback;
+}
+
+#endif // USB_FLASH_DRIVE_SUPPORT
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.h b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.h
new file mode 100644
index 0000000..5c3b852
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.h
@@ -0,0 +1,58 @@
+/**************
+ * usb_host.h *
+ **************/
+
+/****************************************************************************
+ * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
+ * *
+ * This program 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. *
+ * *
+ * This program 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. *
+ * *
+ * To view a copy of the GNU General Public License, go to the following *
+ * location: <https://www.gnu.org/licenses/>. *
+ ****************************************************************************/
+
+#pragma once
+
+/* This the following comes from "lib/usbhost.h", but has been rewritten
+ * to use the SPI functions from Marlin's HAL */
+
+class MAX3421e {
+ private:
+ static uint8_t vbusState;
+ void cs();
+ void ncs();
+
+ uint8_t GpxHandler();
+ uint8_t IntHandler();
+
+ public:
+ bool start();
+
+ void regWr(uint8_t reg, uint8_t data);
+ uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p);
+ void gpioWr(uint8_t data);
+ uint8_t regRd(uint8_t reg);
+ uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p);
+ uint8_t gpioRd();
+ bool reset();
+
+ uint8_t getVbusState() {return vbusState;};
+
+ void busprobe();
+
+ uint8_t Task();
+};
+
+#define USE_MARLIN_MAX3421E
+
+#if defined(__SAM3X8E__) && !defined(ARDUINO_SAM_DUE)
+ #define ARDUINO_SAM_DUE // Spoof the USB library that this is a DUE
+#endif