diff options
author | Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> | 2021-03-04 20:54:23 +0300 |
---|---|---|
committer | Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> | 2021-03-04 20:54:23 +0300 |
commit | e8701195e66f2d27ffe17fb514eae8173795aaf7 (patch) | |
tree | 9f519c4abf6556b9ae7190a6210d87ead1dfadde /Marlin/src/sd/usb_flashdrive/lib-uhs2 | |
download | kp3s-lgvl-e8701195e66f2d27ffe17fb514eae8173795aaf7.tar.xz kp3s-lgvl-e8701195e66f2d27ffe17fb514eae8173795aaf7.zip |
Initial commit
Diffstat (limited to 'Marlin/src/sd/usb_flashdrive/lib-uhs2')
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 |