Add Marlin firmware. Has initial configuration for CoreXY and 1 servo.

Update CAD file with improved clearances for Pilot G2 cartridge clearances after first print.
This commit is contained in:
bisse
2021-10-03 20:35:11 -05:00
parent 814a0bbba3
commit 9babd991e2
2349 changed files with 1512972 additions and 0 deletions

View File

@ -0,0 +1,678 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* Arduino Sd2Card Library
* Copyright (c) 2009 by William Greiman
* Updated with backports of the latest SdFat library from the same author
*
* This file is part of the Arduino Sd2Card Library
*/
#include "../inc/MarlinConfig.h"
#if NEED_SD2CARD_SPI
/* Enable FAST CRC computations - You can trade speed for FLASH space if
* needed by disabling the following define */
#define FAST_CRC 1
#include "Sd2Card.h"
#include "../MarlinCore.h"
#if ENABLED(SD_CHECK_AND_RETRY)
static bool crcSupported = true;
#ifdef FAST_CRC
static const uint8_t crctab7[] PROGMEM = {
0x00,0x09,0x12,0x1B,0x24,0x2D,0x36,0x3F,0x48,0x41,0x5A,0x53,0x6C,0x65,0x7E,0x77,
0x19,0x10,0x0B,0x02,0x3D,0x34,0x2F,0x26,0x51,0x58,0x43,0x4A,0x75,0x7C,0x67,0x6E,
0x32,0x3B,0x20,0x29,0x16,0x1F,0x04,0x0D,0x7A,0x73,0x68,0x61,0x5E,0x57,0x4C,0x45,
0x2B,0x22,0x39,0x30,0x0F,0x06,0x1D,0x14,0x63,0x6A,0x71,0x78,0x47,0x4E,0x55,0x5C,
0x64,0x6D,0x76,0x7F,0x40,0x49,0x52,0x5B,0x2C,0x25,0x3E,0x37,0x08,0x01,0x1A,0x13,
0x7D,0x74,0x6F,0x66,0x59,0x50,0x4B,0x42,0x35,0x3C,0x27,0x2E,0x11,0x18,0x03,0x0A,
0x56,0x5F,0x44,0x4D,0x72,0x7B,0x60,0x69,0x1E,0x17,0x0C,0x05,0x3A,0x33,0x28,0x21,
0x4F,0x46,0x5D,0x54,0x6B,0x62,0x79,0x70,0x07,0x0E,0x15,0x1C,0x23,0x2A,0x31,0x38,
0x41,0x48,0x53,0x5A,0x65,0x6C,0x77,0x7E,0x09,0x00,0x1B,0x12,0x2D,0x24,0x3F,0x36,
0x58,0x51,0x4A,0x43,0x7C,0x75,0x6E,0x67,0x10,0x19,0x02,0x0B,0x34,0x3D,0x26,0x2F,
0x73,0x7A,0x61,0x68,0x57,0x5E,0x45,0x4C,0x3B,0x32,0x29,0x20,0x1F,0x16,0x0D,0x04,
0x6A,0x63,0x78,0x71,0x4E,0x47,0x5C,0x55,0x22,0x2B,0x30,0x39,0x06,0x0F,0x14,0x1D,
0x25,0x2C,0x37,0x3E,0x01,0x08,0x13,0x1A,0x6D,0x64,0x7F,0x76,0x49,0x40,0x5B,0x52,
0x3C,0x35,0x2E,0x27,0x18,0x11,0x0A,0x03,0x74,0x7D,0x66,0x6F,0x50,0x59,0x42,0x4B,
0x17,0x1E,0x05,0x0C,0x33,0x3A,0x21,0x28,0x5F,0x56,0x4D,0x44,0x7B,0x72,0x69,0x60,
0x0E,0x07,0x1C,0x15,0x2A,0x23,0x38,0x31,0x46,0x4F,0x54,0x5D,0x62,0x6B,0x70,0x79
};
static uint8_t CRC7(const uint8_t *data, uint8_t n) {
uint8_t crc = 0;
while (n > 0) {
crc = pgm_read_byte(&crctab7[ (crc << 1) ^ *data++ ]);
n--;
}
return (crc << 1) | 1;
}
#else
static uint8_t CRC7(const uint8_t *data, uint8_t n) {
uint8_t crc = 0;
LOOP_L_N(i, n) {
uint8_t d = data[i];
d ^= crc << 1;
if (d & 0x80) d ^= 9;
crc = d ^ (crc & 0x78) ^ (crc << 4) ^ ((crc >> 3) & 15);
crc &= 0x7F;
}
crc = (crc << 1) ^ (crc << 4) ^ (crc & 0x70) ^ ((crc >> 3) & 0x0F);
return crc | 1;
}
#endif
#endif
// Send command and return error code. Return zero for OK
uint8_t DiskIODriver_SPI_SD::cardCommand(const uint8_t cmd, const uint32_t arg) {
#if ENABLED(SDCARD_COMMANDS_SPLIT)
if (cmd != CMD12) chipDeselect();
#endif
// Select card
chipSelect();
// Wait up to 300 ms if busy
waitNotBusy(SD_WRITE_TIMEOUT);
uint8_t *pa = (uint8_t *)(&arg);
#if ENABLED(SD_CHECK_AND_RETRY)
// Form message
uint8_t d[6] = {(uint8_t) (cmd | 0x40), pa[3], pa[2], pa[1], pa[0] };
// Add crc
d[5] = CRC7(d, 5);
// Send message
LOOP_L_N(k, 6) spiSend(d[k]);
#else
// Send command
spiSend(cmd | 0x40);
// Send argument
for (int8_t i = 3; i >= 0; i--) spiSend(pa[i]);
// Send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA
spiSend(cmd == CMD0 ? 0X95 : 0X87);
#endif
// Skip stuff byte for stop read
if (cmd == CMD12) spiRec();
// Wait for response
for (uint8_t i = 0; ((status_ = spiRec()) & 0x80) && i != 0xFF; i++) { /* Intentionally left empty */ }
return status_;
}
/**
* Determine the size of an SD flash memory card.
*
* \return The number of 512 byte data blocks in the card
* or zero if an error occurs.
*/
uint32_t DiskIODriver_SPI_SD::cardSize() {
csd_t csd;
if (!readCSD(&csd)) return 0;
if (csd.v1.csd_ver == 0) {
uint8_t read_bl_len = csd.v1.read_bl_len;
uint16_t c_size = (csd.v1.c_size_high << 10)
| (csd.v1.c_size_mid << 2) | csd.v1.c_size_low;
uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1)
| csd.v1.c_size_mult_low;
return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
}
else if (csd.v2.csd_ver == 1) {
uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16)
| (csd.v2.c_size_mid << 8) | csd.v2.c_size_low;
return (c_size + 1) << 10;
}
else {
error(SD_CARD_ERROR_BAD_CSD);
return 0;
}
}
void DiskIODriver_SPI_SD::chipDeselect() {
extDigitalWrite(chipSelectPin_, HIGH);
spiSend(0xFF); // Ensure MISO goes high impedance
}
void DiskIODriver_SPI_SD::chipSelect() {
spiInit(spiRate_);
extDigitalWrite(chipSelectPin_, LOW);
}
/**
* Erase a range of blocks.
*
* \param[in] firstBlock The address of the first block in the range.
* \param[in] lastBlock The address of the last block in the range.
*
* \note This function requests the SD card to do a flash erase for a
* range of blocks. The data on the card after an erase operation is
* either 0 or 1, depends on the card vendor. The card must support
* single block erase.
*
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::erase(uint32_t firstBlock, uint32_t lastBlock) {
if (ENABLED(SDCARD_READONLY)) return false;
csd_t csd;
if (!readCSD(&csd)) goto FAIL;
// check for single block erase
if (!csd.v1.erase_blk_en) {
// erase size mask
uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) {
// error card can't erase specified area
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
goto FAIL;
}
}
if (type_ != SD_CARD_TYPE_SDHC) { firstBlock <<= 9; lastBlock <<= 9; }
if (cardCommand(CMD32, firstBlock) || cardCommand(CMD33, lastBlock) || cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
goto FAIL;
}
if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
error(SD_CARD_ERROR_ERASE_TIMEOUT);
goto FAIL;
}
chipDeselect();
return true;
FAIL:
chipDeselect();
return false;
}
/**
* Determine if card supports single block erase.
*
* \return true if single block erase is supported.
* false if single block erase is not supported.
*/
bool DiskIODriver_SPI_SD::eraseSingleBlockEnable() {
csd_t csd;
return readCSD(&csd) ? csd.v1.erase_blk_en : false;
}
/**
* Initialize an SD flash memory card.
*
* \param[in] sckRateID SPI clock rate selector. See setSckRate().
* \param[in] chipSelectPin SD chip select pin number.
*
* \return true for success, false for failure.
* The reason for failure can be determined by calling errorCode() and errorData().
*/
bool DiskIODriver_SPI_SD::init(const uint8_t sckRateID, const pin_t chipSelectPin) {
#if IS_TEENSY_35_36 || IS_TEENSY_40_41
chipSelectPin_ = BUILTIN_SDCARD;
const uint8_t ret = SDHC_CardInit();
type_ = SDHC_CardGetType();
return (ret == 0);
#endif
errorCode_ = type_ = 0;
chipSelectPin_ = chipSelectPin;
// 16-bit init start time allows over a minute
const millis_t init_timeout = millis() + SD_INIT_TIMEOUT;
uint32_t arg;
watchdog_refresh(); // In case init takes too long
// Set pin modes
#if ENABLED(ZONESTAR_12864OLED)
if (chipSelectPin_ != DOGLCD_CS) {
SET_OUTPUT(DOGLCD_CS);
WRITE(DOGLCD_CS, HIGH);
}
#else
extDigitalWrite(chipSelectPin_, HIGH); // For some CPUs pinMode can write the wrong data so init desired data value first
pinMode(chipSelectPin_, OUTPUT); // Solution for #8746 by @benlye
#endif
spiBegin();
// Set SCK rate for initialization commands
spiRate_ = SPI_SD_INIT_RATE;
spiInit(spiRate_);
// Must supply min of 74 clock cycles with CS high.
LOOP_L_N(i, 10) spiSend(0xFF);
watchdog_refresh(); // In case init takes too long
// Command to go idle in SPI mode
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
if (ELAPSED(millis(), init_timeout)) {
error(SD_CARD_ERROR_CMD0);
goto FAIL;
}
}
#if ENABLED(SD_CHECK_AND_RETRY)
crcSupported = (cardCommand(CMD59, 1) == R1_IDLE_STATE);
#endif
watchdog_refresh(); // In case init takes too long
// check SD version
for (;;) {
if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) {
type(SD_CARD_TYPE_SD1);
break;
}
// Get the last byte of r7 response
LOOP_L_N(i, 4) status_ = spiRec();
if (status_ == 0xAA) {
type(SD_CARD_TYPE_SD2);
break;
}
if (ELAPSED(millis(), init_timeout)) {
error(SD_CARD_ERROR_CMD8);
goto FAIL;
}
}
watchdog_refresh(); // In case init takes too long
// Initialize card and send host supports SDHC if SD2
arg = type() == SD_CARD_TYPE_SD2 ? 0x40000000 : 0;
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// Check for timeout
if (ELAPSED(millis(), init_timeout)) {
error(SD_CARD_ERROR_ACMD41);
goto FAIL;
}
}
// If SD2 read OCR register to check for SDHC card
if (type() == SD_CARD_TYPE_SD2) {
if (cardCommand(CMD58, 0)) {
error(SD_CARD_ERROR_CMD58);
goto FAIL;
}
if ((spiRec() & 0xC0) == 0xC0) type(SD_CARD_TYPE_SDHC);
// Discard rest of ocr - contains allowed voltage range
LOOP_L_N(i, 3) spiRec();
}
chipDeselect();
ready = true;
return setSckRate(sckRateID);
FAIL:
chipDeselect();
ready = false;
return false;
}
/**
* Read a 512 byte block from an SD card.
*
* \param[in] blockNumber Logical block to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::readBlock(uint32_t blockNumber, uint8_t *dst) {
#if IS_TEENSY_35_36 || IS_TEENSY_40_41
return 0 == SDHC_CardReadBlock(dst, blockNumber);
#endif
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; // Use address if not SDHC card
#if ENABLED(SD_CHECK_AND_RETRY)
uint8_t retryCnt = 3;
for (;;) {
if (cardCommand(CMD17, blockNumber))
error(SD_CARD_ERROR_CMD17);
else if (readData(dst, 512))
return true;
chipDeselect();
if (!--retryCnt) break;
cardCommand(CMD12, 0); // Try sending a stop command, ignore the result.
errorCode_ = 0;
}
return false;
#else
if (cardCommand(CMD17, blockNumber)) {
error(SD_CARD_ERROR_CMD17);
chipDeselect();
return false;
}
else
return readData(dst, 512);
#endif
}
/**
* Read one data block in a multiple block read sequence
*
* \param[in] dst Pointer to the location for the data to be read.
*
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::readData(uint8_t *dst) {
chipSelect();
return readData(dst, 512);
}
#if ENABLED(SD_CHECK_AND_RETRY)
#ifdef FAST_CRC
static const uint16_t crctab16[] PROGMEM = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
// faster CRC-CCITT
// uses the x^16,x^12,x^5,x^1 polynomial.
static uint16_t CRC_CCITT(const uint8_t *data, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = pgm_read_word(&crctab16[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8);
}
return crc;
}
#else
// slower CRC-CCITT
// uses the x^16,x^12,x^5,x^1 polynomial.
static uint16_t CRC_CCITT(const uint8_t *data, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = (uint8_t)(crc >> 8) | (crc << 8);
crc ^= data[i];
crc ^= (uint8_t)(crc & 0xFF) >> 4;
crc ^= crc << 12;
crc ^= (crc & 0xFF) << 5;
}
return crc;
}
#endif
#endif // SD_CHECK_AND_RETRY
bool DiskIODriver_SPI_SD::readData(uint8_t *dst, const uint16_t count) {
bool success = false;
const millis_t read_timeout = millis() + SD_READ_TIMEOUT;
while ((status_ = spiRec()) == 0xFF) { // Wait for start block token
if (ELAPSED(millis(), read_timeout)) {
error(SD_CARD_ERROR_READ_TIMEOUT);
goto FAIL;
}
}
if (status_ == DATA_START_BLOCK) {
spiRead(dst, count); // Transfer data
const uint16_t recvCrc = (spiRec() << 8) | spiRec();
#if ENABLED(SD_CHECK_AND_RETRY)
success = !crcSupported || recvCrc == CRC_CCITT(dst, count);
if (!success) error(SD_CARD_ERROR_READ_CRC);
#else
success = true;
UNUSED(recvCrc);
#endif
}
else
error(SD_CARD_ERROR_READ);
FAIL:
chipDeselect();
return success;
}
/** read CID or CSR register */
bool DiskIODriver_SPI_SD::readRegister(const uint8_t cmd, void *buf) {
uint8_t *dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
chipDeselect();
return false;
}
return readData(dst, 16);
}
/**
* Start a read multiple blocks sequence.
*
* \param[in] blockNumber Address of first block in sequence.
*
* \note This function is used with readData() and readStop() for optimized
* multiple block reads. SPI chipSelect must be low for the entire sequence.
*
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::readStart(uint32_t blockNumber) {
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
const bool success = !cardCommand(CMD18, blockNumber);
if (!success) error(SD_CARD_ERROR_CMD18);
chipDeselect();
return success;
}
/**
* End a read multiple blocks sequence.
*
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::readStop() {
chipSelect();
const bool success = !cardCommand(CMD12, 0);
if (!success) error(SD_CARD_ERROR_CMD12);
chipDeselect();
return success;
}
/**
* Set the SPI clock rate.
*
* \param[in] sckRateID A value in the range [0, 6].
*
* The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum
* SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128
* for \a scsRateID = 6.
*
* \return The value one, true, is returned for success and the value zero,
* false, is returned for an invalid value of \a sckRateID.
*/
bool DiskIODriver_SPI_SD::setSckRate(const uint8_t sckRateID) {
const bool success = (sckRateID <= 6);
if (success) spiRate_ = sckRateID; else error(SD_CARD_ERROR_SCK_RATE);
return success;
}
/**
* Wait for card to become not-busy
* \param[in] timeout_ms Timeout to abort.
* \return true for success, false for timeout.
*/
bool DiskIODriver_SPI_SD::waitNotBusy(const millis_t timeout_ms) {
const millis_t wait_timeout = millis() + timeout_ms;
while (spiRec() != 0xFF) if (ELAPSED(millis(), wait_timeout)) return false;
return true;
}
void DiskIODriver_SPI_SD::error(const uint8_t code) { errorCode_ = code; }
/**
* Write a 512 byte block to an SD card.
*
* \param[in] blockNumber Logical block to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::writeBlock(uint32_t blockNumber, const uint8_t *src) {
if (ENABLED(SDCARD_READONLY)) return false;
#if IS_TEENSY_35_36 || IS_TEENSY_40_41
return 0 == SDHC_CardWriteBlock(src, blockNumber);
#endif
bool success = false;
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; // Use address if not SDHC card
if (!cardCommand(CMD24, blockNumber)) {
if (writeData(DATA_START_BLOCK, src)) {
if (waitNotBusy(SD_WRITE_TIMEOUT)) { // Wait for flashing to complete
success = !(cardCommand(CMD13, 0) || spiRec()); // Response is r2 so get and check two bytes for nonzero
if (!success) error(SD_CARD_ERROR_WRITE_PROGRAMMING);
}
else
error(SD_CARD_ERROR_WRITE_TIMEOUT);
}
}
else
error(SD_CARD_ERROR_CMD24);
chipDeselect();
return success;
}
/**
* Write one data block in a multiple block write sequence
* \param[in] src Pointer to the location of the data to be written.
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::writeData(const uint8_t *src) {
if (ENABLED(SDCARD_READONLY)) return false;
bool success = true;
chipSelect();
// Wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
success = false;
}
chipDeselect();
return success;
}
// Send one block of data for write block or write multiple blocks
bool DiskIODriver_SPI_SD::writeData(const uint8_t token, const uint8_t *src) {
if (ENABLED(SDCARD_READONLY)) return false;
const uint16_t crc = TERN(SD_CHECK_AND_RETRY, CRC_CCITT(src, 512), 0xFFFF);
spiSendBlock(token, src);
spiSend(crc >> 8);
spiSend(crc & 0xFF);
status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE);
chipDeselect();
return false;
}
return true;
}
/**
* Start a write multiple blocks sequence.
*
* \param[in] blockNumber Address of first block in sequence.
* \param[in] eraseCount The number of blocks to be pre-erased.
*
* \note This function is used with writeData() and writeStop()
* for optimized multiple block writes.
*
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::writeStart(uint32_t blockNumber, const uint32_t eraseCount) {
if (ENABLED(SDCARD_READONLY)) return false;
bool success = false;
if (!cardAcmd(ACMD23, eraseCount)) { // Send pre-erase count
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; // Use address if not SDHC card
success = !cardCommand(CMD25, blockNumber);
if (!success) error(SD_CARD_ERROR_CMD25);
}
else
error(SD_CARD_ERROR_ACMD23);
chipDeselect();
return success;
}
/**
* End a write multiple blocks sequence.
*
* \return true for success, false for failure.
*/
bool DiskIODriver_SPI_SD::writeStop() {
if (ENABLED(SDCARD_READONLY)) return false;
bool success = false;
chipSelect();
if (waitNotBusy(SD_WRITE_TIMEOUT)) {
spiSend(STOP_TRAN_TOKEN);
success = waitNotBusy(SD_WRITE_TIMEOUT);
}
else
error(SD_CARD_ERROR_STOP_TRAN);
chipDeselect();
return success;
}
#endif // NEED_SD2CARD_SPI

View File

@ -0,0 +1,197 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* \file
* \brief Sd2Card class for V2 SD/SDHC cards
*/
/**
* Arduino Sd2Card Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "SdFatConfig.h"
#include "SdInfo.h"
#include "disk_io_driver.h"
#include <stdint.h>
uint16_t const SD_INIT_TIMEOUT = 2000, // (ms) Init timeout
SD_ERASE_TIMEOUT = 10000, // (ms) Erase timeout
SD_READ_TIMEOUT = 300, // (ms) Read timeout
SD_WRITE_TIMEOUT = 600; // (ms) Write timeout
// SD card errors
typedef enum : uint8_t {
SD_CARD_ERROR_CMD0 = 0x01, // Timeout error for command CMD0 (initialize card in SPI mode)
SD_CARD_ERROR_CMD8 = 0x02, // CMD8 was not accepted - not a valid SD card
SD_CARD_ERROR_CMD12 = 0x03, // Card returned an error response for CMD12 (write stop)
SD_CARD_ERROR_CMD17 = 0x04, // Card returned an error response for CMD17 (read block)
SD_CARD_ERROR_CMD18 = 0x05, // Card returned an error response for CMD18 (read multiple block)
SD_CARD_ERROR_CMD24 = 0x06, // Card returned an error response for CMD24 (write block)
SD_CARD_ERROR_CMD25 = 0x07, // WRITE_MULTIPLE_BLOCKS command failed
SD_CARD_ERROR_CMD58 = 0x08, // Card returned an error response for CMD58 (read OCR)
SD_CARD_ERROR_ACMD23 = 0x09, // SET_WR_BLK_ERASE_COUNT failed
SD_CARD_ERROR_ACMD41 = 0x0A, // ACMD41 initialization process timeout
SD_CARD_ERROR_BAD_CSD = 0x0B, // Card returned a bad CSR version field
SD_CARD_ERROR_ERASE = 0x0C, // Erase block group command failed
SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0x0D, // Card not capable of single block erase
SD_CARD_ERROR_ERASE_TIMEOUT = 0x0E, // Erase sequence timed out
SD_CARD_ERROR_READ = 0x0F, // Card returned an error token instead of read data
SD_CARD_ERROR_READ_REG = 0x10, // Read CID or CSD failed
SD_CARD_ERROR_READ_TIMEOUT = 0x11, // Timeout while waiting for start of read data
SD_CARD_ERROR_STOP_TRAN = 0x12, // Card did not accept STOP_TRAN_TOKEN
SD_CARD_ERROR_WRITE = 0x13, // Card returned an error token as a response to a write operation
SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0x14, // REMOVE - not used ... attempt to write protected block zero
SD_CARD_ERROR_WRITE_MULTIPLE = 0x15, // Card did not go ready for a multiple block write
SD_CARD_ERROR_WRITE_PROGRAMMING = 0x16, // Card returned an error to a CMD13 status check after a write
SD_CARD_ERROR_WRITE_TIMEOUT = 0x17, // Timeout occurred during write programming
SD_CARD_ERROR_SCK_RATE = 0x18, // Incorrect rate selected
SD_CARD_ERROR_INIT_NOT_CALLED = 0x19, // Init() not called
// 0x1A is unused now, it was: card returned an error for CMD59 (CRC_ON_OFF)
SD_CARD_ERROR_READ_CRC = 0x1B // Invalid read CRC
} sd_error_code_t;
// card types
uint8_t const SD_CARD_TYPE_SD1 = 1, // Standard capacity V1 SD card
SD_CARD_TYPE_SD2 = 2, // Standard capacity V2 SD card
SD_CARD_TYPE_SDHC = 3; // High Capacity SD card
/**
* Define SOFTWARE_SPI to use bit-bang SPI
*/
#if EITHER(MEGA_SOFT_SPI, USE_SOFTWARE_SPI)
#define SOFTWARE_SPI
#endif
#if IS_TEENSY_35_36 || IS_TEENSY_40_41
#include "NXP_SDHC.h"
#define BUILTIN_SDCARD 254
#endif
/**
* \class Sd2Card
* \brief Raw access to SD and SDHC flash memory cards.
*/
class DiskIODriver_SPI_SD : public DiskIODriver {
public:
DiskIODriver_SPI_SD() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {}
bool erase(uint32_t firstBlock, uint32_t lastBlock);
bool eraseSingleBlockEnable();
/**
* Set SD error code.
* \param[in] code value for error code.
*/
void error(const uint8_t code);
/**
* \return error code for last error. See Sd2Card.h for a list of error codes.
*/
inline int errorCode() const { return errorCode_; }
/** \return error data for last error. */
inline int errorData() const { return status_; }
/**
* Initialize an SD flash memory card with default clock rate and chip
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*
* \return true for success or false for failure.
*/
bool init(const uint8_t sckRateID, const pin_t chipSelectPin) override;
bool setSckRate(const uint8_t sckRateID);
/**
* Return the card type: SD V1, SD V2 or SDHC
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
*/
int type() const { return type_; }
/**
* Read a card's CID register. The CID contains card identification
* information such as Manufacturer ID, Product name, Product serial
* number and Manufacturing date.
*
* \param[out] cid pointer to area for returned data.
*
* \return true for success or false for failure.
*/
bool readCID(cid_t *cid) { return readRegister(CMD10, cid); }
/**
* Read a card's CSD register. The CSD contains Card-Specific Data that
* provides information regarding access to the card's contents.
*
* \param[out] csd pointer to area for returned data.
*
* \return true for success or false for failure.
*/
inline bool readCSD(csd_t *csd) override { return readRegister(CMD9, csd); }
bool readData(uint8_t *dst) override;
bool readStart(uint32_t blockNumber) override;
bool readStop() override;
bool writeData(const uint8_t *src) override;
bool writeStart(const uint32_t blockNumber, const uint32_t eraseCount) override;
bool writeStop() override;
bool readBlock(uint32_t block, uint8_t *dst) override;
bool writeBlock(uint32_t blockNumber, const uint8_t *src) override;
uint32_t cardSize() override;
bool isReady() override { return ready; };
void idle() override {}
private:
bool ready = false;
uint8_t chipSelectPin_,
errorCode_,
spiRate_,
status_,
type_;
// private functions
inline uint8_t cardAcmd(const uint8_t cmd, const uint32_t arg) {
cardCommand(CMD55, 0);
return cardCommand(cmd, arg);
}
uint8_t cardCommand(const uint8_t cmd, const uint32_t arg);
bool readData(uint8_t *dst, const uint16_t count);
bool readRegister(const uint8_t cmd, void *buf);
void chipDeselect();
void chipSelect();
inline void type(const uint8_t value) { type_ = value; }
bool waitNotBusy(const millis_t timeout_ms);
bool writeData(const uint8_t token, const uint8_t *src);
};

View File

@ -0,0 +1,55 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "../inc/MarlinConfig.h"
#include "SdInfo.h"
#include "disk_io_driver.h"
bool SDIO_Init();
bool SDIO_ReadBlock(uint32_t block, uint8_t *dst);
bool SDIO_WriteBlock(uint32_t block, const uint8_t *src);
class DiskIODriver_SDIO : public DiskIODriver {
public:
bool init(const uint8_t sckRateID=0, const pin_t chipSelectPin=0) override { return SDIO_Init(); }
bool readCSD(csd_t *csd) override { return false; }
bool readStart(const uint32_t block) override { return false; }
bool readData(uint8_t *dst) override { return false; }
bool readStop() override { return false; }
bool writeStart(const uint32_t block, const uint32_t) override { return false; }
bool writeData(const uint8_t *src) override { return false; }
bool writeStop() override { return false; }
bool readBlock(uint32_t block, uint8_t *dst) override { return SDIO_ReadBlock(block, dst); }
bool writeBlock(uint32_t block, const uint8_t *src) override { return SDIO_WriteBlock(block, src); }
uint32_t cardSize() override { return 0; }
bool isReady() override { return true; }
void idle() override {}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,384 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* sd/SdBaseFile.h
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "SdFatConfig.h"
#include "SdVolume.h"
#include <stdint.h>
/**
* \struct filepos_t
* \brief internal type for istream
* do not use in user apps
*/
struct filepos_t {
uint32_t position; // stream byte position
uint32_t cluster; // cluster of position
filepos_t() : position(0), cluster(0) {}
};
// use the gnu style oflag in open()
uint8_t const O_READ = 0x01, // open() oflag for reading
O_RDONLY = O_READ, // open() oflag - same as O_IN
O_WRITE = 0x02, // open() oflag for write
O_WRONLY = O_WRITE, // open() oflag - same as O_WRITE
O_RDWR = (O_READ | O_WRITE), // open() oflag for reading and writing
O_ACCMODE = (O_READ | O_WRITE), // open() oflag mask for access modes
O_APPEND = 0x04, // The file offset shall be set to the end of the file prior to each write.
O_SYNC = 0x08, // Synchronous writes - call sync() after each write
O_TRUNC = 0x10, // Truncate the file to zero length
O_AT_END = 0x20, // Set the initial position at the end of the file
O_CREAT = 0x40, // Create the file if nonexistent
O_EXCL = 0x80; // If O_CREAT and O_EXCL are set, open() shall fail if the file exists
// SdBaseFile class static and const definitions
// flags for ls()
uint8_t const LS_DATE = 1, // ls() flag to print modify date
LS_SIZE = 2, // ls() flag to print file size
LS_R = 4; // ls() flag for recursive list of subdirectories
// flags for timestamp
uint8_t const T_ACCESS = 1, // Set the file's last access date
T_CREATE = 2, // Set the file's creation date and time
T_WRITE = 4; // Set the file's write date and time
// values for type_
uint8_t const FAT_FILE_TYPE_CLOSED = 0, // This file has not been opened.
FAT_FILE_TYPE_NORMAL = 1, // A normal file
FAT_FILE_TYPE_ROOT_FIXED = 2, // A FAT12 or FAT16 root directory
FAT_FILE_TYPE_ROOT32 = 3, // A FAT32 root directory
FAT_FILE_TYPE_SUBDIR = 4, // A subdirectory file
FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; // Test value for directory type
/**
* date field for FAT directory entry
* \param[in] year [1980,2107]
* \param[in] month [1,12]
* \param[in] day [1,31]
*
* \return Packed date for dir_t entry.
*/
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { return (year - 1980) << 9 | month << 5 | day; }
/**
* year part of FAT directory date field
* \param[in] fatDate Date in packed dir format.
*
* \return Extracted year [1980,2107]
*/
static inline uint16_t FAT_YEAR(uint16_t fatDate) { return 1980 + (fatDate >> 9); }
/**
* month part of FAT directory date field
* \param[in] fatDate Date in packed dir format.
*
* \return Extracted month [1,12]
*/
static inline uint8_t FAT_MONTH(uint16_t fatDate) { return (fatDate >> 5) & 0xF; }
/**
* day part of FAT directory date field
* \param[in] fatDate Date in packed dir format.
*
* \return Extracted day [1,31]
*/
static inline uint8_t FAT_DAY(uint16_t fatDate) { return fatDate & 0x1F; }
/**
* time field for FAT directory entry
* \param[in] hour [0,23]
* \param[in] minute [0,59]
* \param[in] second [0,59]
*
* \return Packed time for dir_t entry.
*/
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { return hour << 11 | minute << 5 | second >> 1; }
/**
* hour part of FAT directory time field
* \param[in] fatTime Time in packed dir format.
*
* \return Extracted hour [0,23]
*/
static inline uint8_t FAT_HOUR(uint16_t fatTime) { return fatTime >> 11; }
/**
* minute part of FAT directory time field
* \param[in] fatTime Time in packed dir format.
*
* \return Extracted minute [0,59]
*/
static inline uint8_t FAT_MINUTE(uint16_t fatTime) { return (fatTime >> 5) & 0x3F; }
/**
* second part of FAT directory time field
* Note second/2 is stored in packed time.
*
* \param[in] fatTime Time in packed dir format.
*
* \return Extracted second [0,58]
*/
static inline uint8_t FAT_SECOND(uint16_t fatTime) { return 2 * (fatTime & 0x1F); }
// Default date for file timestamps is 1 Jan 2000
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
// Default time for file timestamp is 1 am
uint16_t const FAT_DEFAULT_TIME = (1 << 11);
/**
* \class SdBaseFile
* \brief Base class for SdFile with Print and C++ streams.
*/
class SdBaseFile {
public:
SdBaseFile() : writeError(false), type_(FAT_FILE_TYPE_CLOSED) {}
SdBaseFile(const char *path, uint8_t oflag);
~SdBaseFile() { if (isOpen()) close(); }
/**
* writeError is set to true if an error occurs during a write().
* Set writeError to false before calling print() and/or write() and check
* for true after calls to print() and/or write().
*/
bool writeError;
// helpers for stream classes
/**
* get position for streams
* \param[out] pos struct to receive position
*/
void getpos(filepos_t *pos);
/**
* set position for streams
* \param[out] pos struct with value for new position
*/
void setpos(filepos_t *pos);
bool close();
bool contiguousRange(uint32_t *bgnBlock, uint32_t *endBlock);
bool createContiguous(SdBaseFile *dirFile,
const char *path, uint32_t size);
/**
* \return The current cluster number for a file or directory.
*/
uint32_t curCluster() const { return curCluster_; }
/**
* \return The current position for a file or directory.
*/
uint32_t curPosition() const { return curPosition_; }
/**
* \return Current working directory
*/
static SdBaseFile *cwd() { return cwd_; }
/**
* Set the date/time callback function
*
* \param[in] dateTime The user's call back function. The callback
* function is of the form:
*
* \code
* void dateTime(uint16_t *date, uint16_t *time) {
* uint16_t year;
* uint8_t month, day, hour, minute, second;
*
* // User gets date and time from GPS or real-time clock here
*
* // return date using FAT_DATE macro to format fields
* *date = FAT_DATE(year, month, day);
*
* // return time using FAT_TIME macro to format fields
* *time = FAT_TIME(hour, minute, second);
* }
* \endcode
*
* Sets the function that is called when a file is created or when
* a file's directory entry is modified by sync(). All timestamps,
* access, creation, and modify, are set when a file is created.
* sync() maintains the last access date and last modify date/time.
*
* See the timestamp() function.
*/
static void dateTimeCallback(
void (*dateTime)(uint16_t *date, uint16_t *time)) {
dateTime_ = dateTime;
}
/**
* Cancel the date/time callback function.
*/
static void dateTimeCallbackCancel() { dateTime_ = 0; }
bool dirEntry(dir_t *dir);
static void dirName(const dir_t& dir, char *name);
bool exists(const char *name);
int16_t fgets(char *str, int16_t num, char *delim = 0);
/**
* \return The total number of bytes in a file or directory.
*/
uint32_t fileSize() const { return fileSize_; }
/**
* \return The first cluster number for a file or directory.
*/
uint32_t firstCluster() const { return firstCluster_; }
/**
* \return True if this is a directory else false.
*/
bool isDir() const { return type_ >= FAT_FILE_TYPE_MIN_DIR; }
/**
* \return True if this is a normal file else false.
*/
bool isFile() const { return type_ == FAT_FILE_TYPE_NORMAL; }
/**
* \return True if this is an open file/directory else false.
*/
bool isOpen() const { return type_ != FAT_FILE_TYPE_CLOSED; }
/**
* \return True if this is a subdirectory else false.
*/
bool isSubDir() const { return type_ == FAT_FILE_TYPE_SUBDIR; }
/**
* \return True if this is the root directory.
*/
bool isRoot() const { return type_ == FAT_FILE_TYPE_ROOT_FIXED || type_ == FAT_FILE_TYPE_ROOT32; }
bool getDosName(char * const name);
void ls(uint8_t flags = 0, uint8_t indent = 0);
bool mkdir(SdBaseFile *dir, const char *path, bool pFlag = true);
bool open(SdBaseFile *dirFile, uint16_t index, uint8_t oflag);
bool open(SdBaseFile *dirFile, const char *path, uint8_t oflag);
bool open(const char *path, uint8_t oflag = O_READ);
bool openNext(SdBaseFile *dirFile, uint8_t oflag);
bool openRoot(SdVolume *vol);
int peek();
static void printFatDate(uint16_t fatDate);
static void printFatTime(uint16_t fatTime);
bool printName();
int16_t read();
int16_t read(void *buf, uint16_t nbyte);
int8_t readDir(dir_t *dir, char *longFilename);
static bool remove(SdBaseFile *dirFile, const char *path);
bool remove();
/**
* Set the file's current position to zero.
*/
void rewind() { seekSet(0); }
bool rename(SdBaseFile *dirFile, const char *newPath);
bool rmdir();
bool rmRfStar();
/**
* Set the files position to current position + \a pos. See seekSet().
* \param[in] offset The new position in bytes from the current position.
* \return true for success or false for failure.
*/
bool seekCur(const int32_t offset) { return seekSet(curPosition_ + offset); }
/**
* Set the files position to end-of-file + \a offset. See seekSet().
* \param[in] offset The new position in bytes from end-of-file.
* \return true for success or false for failure.
*/
bool seekEnd(const int32_t offset = 0) { return seekSet(fileSize_ + offset); }
bool seekSet(const uint32_t pos);
bool sync();
bool timestamp(SdBaseFile *file);
bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second);
/**
* Type of file. Use isFile() or isDir() instead of type() if possible.
*
* \return The file or directory type.
*/
uint8_t type() const { return type_; }
bool truncate(uint32_t size);
/**
* \return SdVolume that contains this file.
*/
SdVolume* volume() const { return vol_; }
int16_t write(const void *buf, uint16_t nbyte);
private:
friend class SdFat; // allow SdFat to set cwd_
static SdBaseFile *cwd_; // global pointer to cwd dir
// data time callback function
static void (*dateTime_)(uint16_t *date, uint16_t *time);
// bits defined in flags_
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC), // should be 0x0F
F_FILE_DIR_DIRTY = 0x80; // sync of directory entry required
// private data
uint8_t flags_; // See above for definition of flags_ bits
uint8_t fstate_; // error and eof indicator
uint8_t type_; // type of file see above for values
uint32_t curCluster_; // cluster for current file position
uint32_t curPosition_; // current file position in bytes from beginning
uint32_t dirBlock_; // block for this files directory entry
uint8_t dirIndex_; // index of directory entry in dirBlock
uint32_t fileSize_; // file size in bytes
uint32_t firstCluster_; // first cluster of file
SdVolume *vol_; // volume where file is located
/**
* EXPERIMENTAL - Don't use!
*/
//bool openParent(SdBaseFile *dir);
// private functions
bool addCluster();
bool addDirCluster();
dir_t* cacheDirEntry(uint8_t action);
int8_t lsPrintNext(uint8_t flags, uint8_t indent);
static bool make83Name(const char *str, uint8_t *name, const char **ptr);
bool mkdir(SdBaseFile *parent, const uint8_t dname[11]);
bool open(SdBaseFile *dirFile, const uint8_t dname[11], uint8_t oflag);
bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
dir_t* readDirCache();
};

View File

@ -0,0 +1,112 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* sd/SdFatConfig.h
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "../inc/MarlinConfig.h"
/**
* To use multiple SD cards set USE_MULTIPLE_CARDS nonzero.
*
* Using multiple cards costs 400 - 500 bytes of flash.
*
* Each card requires about 550 bytes of SRAM so use of a Mega is recommended.
*/
#define USE_MULTIPLE_CARDS 0 //TODO? ENABLED(MULTI_VOLUME)
/**
* Call flush for endl if ENDL_CALLS_FLUSH is nonzero
*
* The standard for iostreams is to call flush. This is very costly for
* SdFat. Each call to flush causes 2048 bytes of I/O to the SD.
*
* SdFat has a single 512 byte buffer for SD I/O so it must write the current
* data block to the SD, read the directory block from the SD, update the
* directory entry, write the directory block to the SD and read the data
* block back into the buffer.
*
* The SD flash memory controller is not designed for this many rewrites
* so performance may be reduced by more than a factor of 100.
*
* If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force
* all data to be written to the SD.
*/
#define ENDL_CALLS_FLUSH 0
/**
* Allow FAT12 volumes if FAT12_SUPPORT is nonzero.
* FAT12 has not been well tested.
*/
#define FAT12_SUPPORT 0
/**
* SPI init rate for SD initialization commands. Must be 5 (F_CPU/64)
* or 6 (F_CPU/128).
*/
#define SPI_SD_INIT_RATE 5
/**
* Set the SS pin high for hardware SPI. If SS is chip select for another SPI
* device this will disable that device during the SD init phase.
*/
#define SET_SPI_SS_HIGH 1
/**
* Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos.
* Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
*
* MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
* on Mega Arduinos. Software SPI works well with GPS Shield V1.1
* but many SD cards will fail with GPS Shield V1.0.
*/
#define MEGA_SOFT_SPI 0
// Set USE_SOFTWARE_SPI nonzero to ALWAYS use Software SPI.
#define USE_SOFTWARE_SPI 0
/**
* The __cxa_pure_virtual function is an error handler that is invoked when
* a pure virtual function is called.
*/
#define USE_CXA_PURE_VIRTUAL 1
/**
* Defines for 8.3 and long (vfat) filenames
*/
#define FILENAME_LENGTH 13 // Number of UTF-16 characters per entry
// UTF-8 may use up to 3 bytes to represent single UTF-16 code point.
// We discard 3-byte characters allowing only 2-bytes
// or 1-byte if UTF_FILENAME_SUPPORT disabled.
#define LONG_FILENAME_CHARSIZE TERN(UTF_FILENAME_SUPPORT, 2, 1)
// Total bytes needed to store a single long filename
#define LONG_FILENAME_LENGTH (FILENAME_LENGTH * LONG_FILENAME_CHARSIZE * MAX_VFAT_ENTRIES + 1)

View File

@ -0,0 +1,609 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* sd/SdFatStructs.h
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include <stdint.h>
#define PACKED __attribute__((__packed__))
/**
* mostly from Microsoft document fatgen103.doc
* https://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
*/
uint8_t const BOOTSIG0 = 0x55, // Value for byte 510 of boot block or MBR
BOOTSIG1 = 0xAA, // Value for byte 511 of boot block or MBR
EXTENDED_BOOT_SIG = 0x29; // Value for bootSignature field int FAT/FAT32 boot sector
/**
* \struct partitionTable
* \brief MBR partition table entry
*
* A partition table entry for a MBR formatted storage device.
* The MBR partition table has four entries.
*/
struct partitionTable {
/**
* Boot Indicator . Indicates whether the volume is the active
* partition. Legal values include: 0x00. Do not use for booting.
* 0x80 Active partition.
*/
uint8_t boot;
/**
* Head part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t beginHead;
/**
* Sector part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned beginSector : 6;
/** High bits cylinder for first block in partition. */
unsigned beginCylinderHigh : 2;
/**
* Combine beginCylinderLow with beginCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t beginCylinderLow;
/**
* Partition type. See defines that begin with PART_TYPE_ for
* some Microsoft partition types.
*/
uint8_t type;
/**
* head part of cylinder-head-sector address of the last sector in the
* partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t endHead;
/**
* Sector part of cylinder-head-sector address of the last sector in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned endSector : 6;
/** High bits of end cylinder */
unsigned endCylinderHigh : 2;
/**
* Combine endCylinderLow with endCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t endCylinderLow;
uint32_t firstSector; // Logical block address of the first block in the partition.
uint32_t totalSectors; // Length of the partition, in blocks.
} PACKED;
typedef struct partitionTable part_t; // Type name for partitionTable
/**
* \struct masterBootRecord
*
* \brief Master Boot Record
*
* The first block of a storage device that is formatted with a MBR.
*/
struct masterBootRecord {
uint8_t codeArea[440]; // Code Area for master boot program.
uint32_t diskSignature; // Optional Windows NT disk signature. May contain boot code.
uint16_t usuallyZero; // Usually zero but may be more boot code.
part_t part[4]; // Partition tables.
uint8_t mbrSig0; // First MBR signature byte. Must be 0x55
uint8_t mbrSig1; // Second MBR signature byte. Must be 0xAA
} PACKED;
/** Type name for masterBootRecord */
typedef struct masterBootRecord mbr_t;
/**
* \struct fat_boot
*
* \brief Boot sector for a FAT12/FAT16 volume.
*/
struct fat_boot {
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next nonexecutable bytes.
*/
uint8_t jump[3];
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
char oemId[8];
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
uint8_t sectorsPerCluster;
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. The value of this field is always 1.
*/
uint16_t reservedSectorCount;
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
uint8_t fatCount;
/**
* For FAT12 and FAT16 volumes, this field contains the count of
* 32-byte directory entries in the root directory. For FAT32 volumes,
* this field must be set to 0. For FAT12 and FAT16 volumes, this
* value should always specify a count that when multiplied by 32
* results in a multiple of bytesPerSector. FAT16 volumes should
* use the value 512.
*/
uint16_t rootDirEntryCount;
/**
* This field is the old 16-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then totalSectors32
* must be nonzero. For FAT32 volumes, this field must be 0. For
* FAT12 and FAT16 volumes, this field contains the sector count, and
* totalSectors32 is 0 if the total sector count fits
* (is less than 0x10000).
*/
uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (nonremovable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType;
/**
* Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16;
uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise.
uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise.
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors;
/**
* This field is the new 32-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then
* totalSectors16 must be nonzero.
*/
uint32_t totalSectors32;
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
uint8_t driveNumber;
uint8_t reserved1; // used by Windows NT - should be zero for FAT
uint8_t bootSignature; // 0x29 if next three fields are valid
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint32_t volumeSerialNumber;
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
char volumeLabel[11];
/**
* A field with a value of either FAT, FAT12 or FAT16,
* depending on the disk format.
*/
char fileSystemType[8];
uint8_t bootCode[448]; // X86 boot code
uint8_t bootSectorSig0; // must be 0x55
uint8_t bootSectorSig1; // must be 0xAA
} PACKED;
typedef struct fat_boot fat_boot_t; // Type name for FAT Boot Sector
/**
* \struct fat32_boot
*
* \brief Boot sector for a FAT32 volume.
*/
struct fat32_boot {
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next nonexecutable bytes.
*/
uint8_t jump[3];
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
char oemId[8];
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
uint8_t sectorsPerCluster;
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. Must not be zero
*/
uint16_t reservedSectorCount;
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
uint8_t fatCount;
/**
* FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
*/
uint16_t rootDirEntryCount;
/**
* For FAT32 volumes, this field must be 0.
*/
uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (nonremovable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType;
/**
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16;
uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise.
uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise.
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors;
/**
* Contains the total number of sectors in the FAT32 volume.
*/
uint32_t totalSectors32;
/**
* Count of sectors occupied by one FAT on FAT32 volumes.
*/
uint32_t sectorsPerFat32;
/**
* This field is only defined for FAT32 media and does not exist on
* FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT.
* Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced
* in bits 0-3.
* Bits 8-15 -- Reserved.
*/
uint16_t fat32Flags;
/**
* FAT32 version. High byte is major revision number.
* Low byte is minor revision number. Only 0.0 define.
*/
uint16_t fat32Version;
/**
* Cluster number of the first cluster of the root directory for FAT32.
* This usually 2 but not required to be 2.
*/
uint32_t fat32RootCluster;
/**
* Sector number of FSINFO structure in the reserved area of the
* FAT32 volume. Usually 1.
*/
uint16_t fat32FSInfo;
/**
* If nonzero, indicates the sector number in the reserved area
* of the volume of a copy of the boot record. Usually 6.
* No value other than 6 is recommended.
*/
uint16_t fat32BackBootBlock;
/**
* Reserved for future expansion. Code that formats FAT32 volumes
* should always set all of the bytes of this field to 0.
*/
uint8_t fat32Reserved[12];
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
uint8_t driveNumber;
uint8_t reserved1; // Used by Windows NT - should be zero for FAT
uint8_t bootSignature; // 0x29 if next three fields are valid
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint32_t volumeSerialNumber;
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
char volumeLabel[11];
/**
* A text field with a value of FAT32.
*/
char fileSystemType[8];
uint8_t bootCode[420]; // X86 boot code
uint8_t bootSectorSig0; // must be 0x55
uint8_t bootSectorSig1; // must be 0xAA
} PACKED;
typedef struct fat32_boot fat32_boot_t; // Type name for FAT32 Boot Sector
uint32_t const FSINFO_LEAD_SIG = 0x41615252, // 'AaRR' Lead signature for a FSINFO sector
FSINFO_STRUCT_SIG = 0x61417272; // 'aArr' Struct signature for a FSINFO sector
/**
* \struct fat32_fsinfo
*
* \brief FSINFO sector for a FAT32 volume.
*/
struct fat32_fsinfo {
uint32_t leadSignature; // must be 0x52, 0x52, 0x61, 0x41 'RRaA'
uint8_t reserved1[480]; // must be zero
uint32_t structSignature; // must be 0x72, 0x72, 0x41, 0x61 'rrAa'
/**
* Contains the last known free cluster count on the volume.
* If the value is 0xFFFFFFFF, then the free count is unknown
* and must be computed. Any other value can be used, but is
* not necessarily correct. It should be range checked at least
* to make sure it is <= volume cluster count.
*/
uint32_t freeCount;
/**
* This is a hint for the FAT driver. It indicates the cluster
* number at which the driver should start looking for free clusters.
* If the value is 0xFFFFFFFF, then there is no hint and the driver
* should start looking at cluster 2.
*/
uint32_t nextFree;
uint8_t reserved2[12]; // must be zero
uint8_t tailSignature[4]; // must be 0x00, 0x00, 0x55, 0xAA
} PACKED;
typedef struct fat32_fsinfo fat32_fsinfo_t; // Type name for FAT32 FSINFO Sector
// End Of Chain values for FAT entries
uint16_t const FAT12EOC = 0xFFF, // FAT12 end of chain value used by Microsoft.
FAT12EOC_MIN = 0xFF8, // Minimum value for FAT12 EOC. Use to test for EOC.
FAT16EOC = 0xFFFF, // FAT16 end of chain value used by Microsoft.
FAT16EOC_MIN = 0xFFF8; // Minimum value for FAT16 EOC. Use to test for EOC.
uint32_t const FAT32EOC = 0x0FFFFFFF, // FAT32 end of chain value used by Microsoft.
FAT32EOC_MIN = 0x0FFFFFF8, // Minimum value for FAT32 EOC. Use to test for EOC.
FAT32MASK = 0x0FFFFFFF; // Mask a for FAT32 entry. Entries are 28 bits.
/**
* \struct directoryEntry
* \brief FAT short directory entry
*
* Short means short 8.3 name, not the entry size.
*
* Date Format. A FAT directory entry date stamp is a 16-bit field that is
* basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
* format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
* 16-bit word):
*
* Bits 9-15: Count of years from 1980, valid value range 0-127
* inclusive (1980-2107).
*
* Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
*
* Bits 0-4: Day of month, valid value range 1-31 inclusive.
*
* Time Format. A FAT directory entry time stamp is a 16-bit field that has
* a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
* 16-bit word, bit 15 is the MSB of the 16-bit word).
*
* Bits 11-15: Hours, valid value range 0-23 inclusive.
*
* Bits 5-10: Minutes, valid value range 0-59 inclusive.
*
* Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
*
* The valid time range is from Midnight 00:00:00 to 23:59:58.
*/
struct directoryEntry {
/**
* Short 8.3 name.
*
* The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill.
*/
uint8_t name[11];
/**
* Entry attributes.
*
* The upper two bits of the attribute byte are reserved and should
* always be set to 0 when a file is created and never modified or
* looked at after that. See defines that begin with DIR_ATT_.
*/
uint8_t attributes;
/**
* Reserved for use by Windows NT. Set value to 0 when a file is
* created and never modify or look at it after that.
*/
uint8_t reservedNT;
/**
* The granularity of the seconds part of creationTime is 2 seconds
* so this field is a count of tenths of a second and it's valid
* value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
uint8_t creationTimeTenths;
uint16_t creationTime; // Time file was created.
uint16_t creationDate; // Date file was created.
/**
* Last access date. Note that there is no last access time, only
* a date. This is the date of last read or write. In the case of
* a write, this should be set to the same date as lastWriteDate.
*/
uint16_t lastAccessDate;
/**
* High word of this entry's first cluster number (always 0 for a
* FAT12 or FAT16 volume).
*/
uint16_t firstClusterHigh;
uint16_t lastWriteTime; // Time of last write. File creation is considered a write.
uint16_t lastWriteDate; // Date of last write. File creation is considered a write.
uint16_t firstClusterLow; // Low word of this entry's first cluster number.
uint32_t fileSize; // 32-bit unsigned holding this file's size in bytes.
} PACKED;
/**
* \struct directoryVFATEntry
* \brief VFAT long filename directory entry
*
* directoryVFATEntries are found in the same list as normal directoryEntry.
* But have the attribute field set to DIR_ATT_LONG_NAME.
*
* Long filenames are saved in multiple directoryVFATEntries.
* Each entry containing 13 UTF-16 characters.
*/
struct directoryVFATEntry {
/**
* Sequence number. Consists of 2 parts:
* bit 6: indicates first long filename block for the next file
* bit 0-4: the position of this long filename block (first block is 1)
*/
uint8_t sequenceNumber;
uint16_t name1[5]; // First set of UTF-16 characters
uint8_t attributes; // attributes (at the same location as in directoryEntry), always 0x0F
uint8_t reservedNT; // Reserved for use by Windows NT. Always 0.
uint8_t checksum; // Checksum of the short 8.3 filename, can be used to checked if the file system as modified by a not-long-filename aware implementation.
uint16_t name2[6]; // Second set of UTF-16 characters
uint16_t firstClusterLow; // firstClusterLow is always zero for longFilenames
uint16_t name3[2]; // Third set of UTF-16 characters
} PACKED;
// Definitions for directory entries
//
typedef struct directoryEntry dir_t; // Type name for directoryEntry
typedef struct directoryVFATEntry vfat_t; // Type name for directoryVFATEntry
uint8_t const DIR_NAME_0xE5 = 0x05, // escape for name[0] = 0xE5
DIR_NAME_DELETED = 0xE5, // name[0] value for entry that is free after being "deleted"
DIR_NAME_FREE = 0x00, // name[0] value for entry that is free and no allocated entries follow
DIR_ATT_READ_ONLY = 0x01, // file is read-only
DIR_ATT_HIDDEN = 0x02, // File should hidden in directory listings
DIR_ATT_SYSTEM = 0x04, // Entry is for a system file
DIR_ATT_VOLUME_ID = 0x08, // Directory entry contains the volume label
DIR_ATT_DIRECTORY = 0x10, // Entry is for a directory
DIR_ATT_ARCHIVE = 0x20, // Old DOS archive bit for backup support
DIR_ATT_LONG_NAME = 0x0F, // Test value for long name entry. Test is (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME.
DIR_ATT_LONG_NAME_MASK = 0x3F, // Test mask for long name entry
DIR_ATT_DEFINED_BITS = 0x3F; // defined attribute bits
/**
* Directory entry is part of a long name
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for part of a long name else false.
*/
static inline uint8_t DIR_IS_LONG_NAME(const dir_t *dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
}
/** Mask for file/subdirectory tests */
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
/**
* Directory entry is for a file
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for a normal file else false.
*/
static inline uint8_t DIR_IS_FILE(const dir_t *dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
}
/**
* Directory entry is for a subdirectory
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for a subdirectory else false.
*/
static inline uint8_t DIR_IS_SUBDIR(const dir_t *dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
}
/**
* Directory entry is for a file or subdirectory
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for a normal file or subdirectory else false.
*/
static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t *dir) {
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
}

View File

@ -0,0 +1,62 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* sd/SdFatUtil.cpp
*
* Arduino SdFat Library
* Copyright (c) 2008 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "../inc/MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "SdFatUtil.h"
#include <string.h>
/**
* Amount of free RAM
* \return The number of free bytes.
*/
#ifdef __arm__
extern "C" char* sbrk(int incr);
int SdFatUtil::FreeRam() {
char top;
return &top - reinterpret_cast<char*>(sbrk(0));
}
#else
extern char* __brkval;
extern char __bss_end;
int SdFatUtil::FreeRam() {
char top;
return __brkval ? &top - __brkval : &top - &__bss_end;
}
#endif
#endif // SDSUPPORT

View File

@ -0,0 +1,42 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* sd/SdFatUtil.h
*
* Arduino SdFat Library
* Copyright (c) 2008 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
/**
* \file
* \brief Useful utility functions.
*/
namespace SdFatUtil {
int FreeRam();
}
using namespace SdFatUtil; // NOLINT

View File

@ -0,0 +1,102 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* sd/SdFile.cpp
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "../inc/MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "SdFile.h"
/**
* Create a file object and open it in the current working directory.
*
* \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
*
* \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
* OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
*/
SdFile::SdFile(const char *path, uint8_t oflag) : SdBaseFile(path, oflag) { }
/**
* Write data to an open file.
*
* \note Data is moved to the cache but may not be written to the
* storage device until sync() is called.
*
* \param[in] buf Pointer to the location of the data to be written.
*
* \param[in] nbyte Number of bytes to write.
*
* \return For success write() returns the number of bytes written, always
* \a nbyte. If an error occurs, write() returns -1. Possible errors
* include write() is called before a file has been opened, write is called
* for a read-only file, device is full, a corrupt file system or an I/O error.
*/
int16_t SdFile::write(const void *buf, uint16_t nbyte) { return SdBaseFile::write(buf, nbyte); }
/**
* Write a byte to a file. Required by the Arduino Print class.
* \param[in] b the byte to be written.
* Use writeError to check for errors.
*/
#if ARDUINO >= 100
size_t SdFile::write(uint8_t b) { return SdBaseFile::write(&b, 1); }
#else
void SdFile::write(uint8_t b) { SdBaseFile::write(&b, 1); }
#endif
/**
* Write a string to a file. Used by the Arduino Print class.
* \param[in] str Pointer to the string.
* Use writeError to check for errors.
*/
void SdFile::write(const char *str) { SdBaseFile::write(str, strlen(str)); }
/**
* Write a PROGMEM string to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use writeError to check for errors.
*/
void SdFile::write_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c);
}
/**
* Write a PROGMEM string followed by CR/LF to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use writeError to check for errors.
*/
void SdFile::writeln_P(PGM_P str) {
write_P(str);
write_P(PSTR("\r\n"));
}
#endif // SDSUPPORT

View File

@ -0,0 +1,56 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* sd/SdFile.h
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "SdBaseFile.h"
#include <stdint.h>
#include <string.h>
/**
* \class SdFile
* \brief SdBaseFile with Print.
*/
class SdFile : public SdBaseFile {
public:
SdFile() {}
SdFile(const char *name, uint8_t oflag);
#if ARDUINO >= 100
size_t write(uint8_t b);
#else
void write(uint8_t b);
#endif
int16_t write(const void *buf, uint16_t nbyte);
void write(const char *str);
void write_P(PGM_P str);
void writeln_P(PGM_P str);
};

View File

@ -0,0 +1,265 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* Arduino Sd2Card Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include <stdint.h>
// Based on the document:
//
// SD Specifications
// Part 1
// Physical Layer
// Simplified Specification
// Version 3.01
// May 18, 2010
//
// https://www.sdcard.org/downloads/pls/index.html
// SD card commands
uint8_t const CMD0 = 0x00, // GO_IDLE_STATE - init card in spi mode if CS low
CMD8 = 0x08, // SEND_IF_COND - verify SD Memory Card interface operating condition
CMD9 = 0x09, // SEND_CSD - read the Card Specific Data (CSD register)
CMD10 = 0x0A, // SEND_CID - read the card identification information (CID register)
CMD12 = 0x0C, // STOP_TRANSMISSION - end multiple block read sequence
CMD13 = 0x0D, // SEND_STATUS - read the card status register
CMD17 = 0x11, // READ_SINGLE_BLOCK - read a single data block from the card
CMD18 = 0x12, // READ_MULTIPLE_BLOCK - read a multiple data blocks from the card
CMD24 = 0x18, // WRITE_BLOCK - write a single data block to the card
CMD25 = 0x19, // WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION
CMD32 = 0x20, // ERASE_WR_BLK_START - sets the address of the first block to be erased
CMD33 = 0x21, // ERASE_WR_BLK_END - sets the address of the last block of the continuous range to be erased
CMD38 = 0x26, // ERASE - erase all previously selected blocks
CMD55 = 0x37, // APP_CMD - escape for application specific command
CMD58 = 0x3A, // READ_OCR - read the OCR register of a card
CMD59 = 0x3B, // CRC_ON_OFF - enable or disable CRC checking
ACMD23 = 0x17, // SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be pre-erased before writing
ACMD41 = 0x29; // SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process
/** status for card in the ready state */
uint8_t const R1_READY_STATE = 0x00;
/** status for card in the idle state */
uint8_t const R1_IDLE_STATE = 0x01;
/** status bit for illegal command */
uint8_t const R1_ILLEGAL_COMMAND = 0x04;
/** start data token for read or write single block*/
uint8_t const DATA_START_BLOCK = 0xFE;
/** stop token for write multiple blocks*/
uint8_t const STOP_TRAN_TOKEN = 0xFD;
/** start data token for write multiple blocks*/
uint8_t const WRITE_MULTIPLE_TOKEN = 0xFC;
/** mask for data response tokens after a write block operation */
uint8_t const DATA_RES_MASK = 0x1F;
/** write data accepted token */
uint8_t const DATA_RES_ACCEPTED = 0x05;
/** Card IDentification (CID) register */
typedef struct CID {
// byte 0
/** Manufacturer ID */
unsigned char mid;
// byte 1-2
/** OEM/Application ID */
char oid[2];
// byte 3-7
/** Product name */
char pnm[5];
// byte 8
/** Product revision least significant digit */
unsigned char prv_m : 4;
/** Product revision most significant digit */
unsigned char prv_n : 4;
// byte 9-12
/** Product serial number */
uint32_t psn;
// byte 13
/** Manufacturing date year low digit */
unsigned char mdt_year_high : 4;
/** not used */
unsigned char reserved : 4;
// byte 14
/** Manufacturing date month */
unsigned char mdt_month : 4;
/** Manufacturing date year low digit */
unsigned char mdt_year_low : 4;
// byte 15
/** not used always 1 */
unsigned char always1 : 1;
/** CRC7 checksum */
unsigned char crc : 7;
} cid_t;
/** CSD for version 1.00 cards */
typedef struct CSDV1 {
// byte 0
unsigned char reserved1 : 6;
unsigned char csd_ver : 2;
// byte 1
unsigned char taac;
// byte 2
unsigned char nsac;
// byte 3
unsigned char tran_speed;
// byte 4
unsigned char ccc_high;
// byte 5
unsigned char read_bl_len : 4;
unsigned char ccc_low : 4;
// byte 6
unsigned char c_size_high : 2;
unsigned char reserved2 : 2;
unsigned char dsr_imp : 1;
unsigned char read_blk_misalign : 1;
unsigned char write_blk_misalign : 1;
unsigned char read_bl_partial : 1;
// byte 7
unsigned char c_size_mid;
// byte 8
unsigned char vdd_r_curr_max : 3;
unsigned char vdd_r_curr_min : 3;
unsigned char c_size_low : 2;
// byte 9
unsigned char c_size_mult_high : 2;
unsigned char vdd_w_cur_max : 3;
unsigned char vdd_w_curr_min : 3;
// byte 10
unsigned char sector_size_high : 6;
unsigned char erase_blk_en : 1;
unsigned char c_size_mult_low : 1;
// byte 11
unsigned char wp_grp_size : 7;
unsigned char sector_size_low : 1;
// byte 12
unsigned char write_bl_len_high : 2;
unsigned char r2w_factor : 3;
unsigned char reserved3 : 2;
unsigned char wp_grp_enable : 1;
// byte 13
unsigned char reserved4 : 5;
unsigned char write_partial : 1;
unsigned char write_bl_len_low : 2;
// byte 14
unsigned char reserved5: 2;
unsigned char file_format : 2;
unsigned char tmp_write_protect : 1;
unsigned char perm_write_protect : 1;
unsigned char copy : 1;
/** Indicates the file format on the card */
unsigned char file_format_grp : 1;
// byte 15
unsigned char always1 : 1;
unsigned char crc : 7;
} csd1_t;
/** CSD for version 2.00 cards */
typedef struct CSDV2 {
// byte 0
unsigned char reserved1 : 6;
unsigned char csd_ver : 2;
// byte 1
/** fixed to 0x0E */
unsigned char taac;
// byte 2
/** fixed to 0 */
unsigned char nsac;
// byte 3
unsigned char tran_speed;
// byte 4
unsigned char ccc_high;
// byte 5
/** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */
unsigned char read_bl_len : 4;
unsigned char ccc_low : 4;
// byte 6
/** not used */
unsigned char reserved2 : 4;
unsigned char dsr_imp : 1;
/** fixed to 0 */
unsigned char read_blk_misalign : 1;
/** fixed to 0 */
unsigned char write_blk_misalign : 1;
/** fixed to 0 - no partial read */
unsigned char read_bl_partial : 1;
// byte 7
/** not used */
unsigned char reserved3 : 2;
/** high part of card size */
unsigned char c_size_high : 6;
// byte 8
/** middle part of card size */
unsigned char c_size_mid;
// byte 9
/** low part of card size */
unsigned char c_size_low;
// byte 10
/** sector size is fixed at 64 KB */
unsigned char sector_size_high : 6;
/** fixed to 1 - erase single is supported */
unsigned char erase_blk_en : 1;
/** not used */
unsigned char reserved4 : 1;
// byte 11
unsigned char wp_grp_size : 7;
/** sector size is fixed at 64 KB */
unsigned char sector_size_low : 1;
// byte 12
/** write_bl_len fixed for 512 byte blocks */
unsigned char write_bl_len_high : 2;
/** fixed value of 2 */
unsigned char r2w_factor : 3;
/** not used */
unsigned char reserved5 : 2;
/** fixed value of 0 - no write protect groups */
unsigned char wp_grp_enable : 1;
// byte 13
unsigned char reserved6 : 5;
/** always zero - no partial block read*/
unsigned char write_partial : 1;
/** write_bl_len fixed for 512 byte blocks */
unsigned char write_bl_len_low : 2;
// byte 14
unsigned char reserved7: 2;
/** Do not use always 0 */
unsigned char file_format : 2;
unsigned char tmp_write_protect : 1;
unsigned char perm_write_protect : 1;
unsigned char copy : 1;
/** Do not use always 0 */
unsigned char file_format_grp : 1;
// byte 15
/** not used always 1 */
unsigned char always1 : 1;
/** checksum */
unsigned char crc : 7;
} csd2_t;
/** union of old and new style CSD register */
union csd_t {
csd1_t v1;
csd2_t v2;
};

View File

@ -0,0 +1,405 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* sd/SdVolume.cpp
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "../inc/MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "SdVolume.h"
#include "../MarlinCore.h"
#if !USE_MULTIPLE_CARDS
// raw block cache
uint32_t SdVolume::cacheBlockNumber_; // current block number
cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card
DiskIODriver *SdVolume::sdCard_; // pointer to SD card object
bool SdVolume::cacheDirty_; // cacheFlush() will write block if true
uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT
#endif
// find a contiguous group of clusters
bool SdVolume::allocContiguous(uint32_t count, uint32_t *curCluster) {
if (ENABLED(SDCARD_READONLY)) return false;
// start of group
uint32_t bgnCluster;
// end of group
uint32_t endCluster;
// last cluster of FAT
uint32_t fatEnd = clusterCount_ + 1;
// flag to save place to start next search
bool setStart;
// set search start cluster
if (*curCluster) {
// try to make file contiguous
bgnCluster = *curCluster + 1;
// don't save new start location
setStart = false;
}
else {
// start at likely place for free cluster
bgnCluster = allocSearchStart_;
// save next search start if one cluster
setStart = count == 1;
}
// end of group
endCluster = bgnCluster;
// search the FAT for free clusters
for (uint32_t n = 0;; n++, endCluster++) {
// can't find space checked all clusters
if (n >= clusterCount_) return false;
// past end - start from beginning of FAT
if (endCluster > fatEnd) {
bgnCluster = endCluster = 2;
}
uint32_t f;
if (!fatGet(endCluster, &f)) return false;
if (f != 0) {
// cluster in use try next cluster as bgnCluster
bgnCluster = endCluster + 1;
}
else if ((endCluster - bgnCluster + 1) == count) {
// done - found space
break;
}
}
// mark end of chain
if (!fatPutEOC(endCluster)) return false;
// link clusters
while (endCluster > bgnCluster) {
if (!fatPut(endCluster - 1, endCluster)) return false;
endCluster--;
}
if (*curCluster != 0) {
// connect chains
if (!fatPut(*curCluster, bgnCluster)) return false;
}
// return first cluster number to caller
*curCluster = bgnCluster;
// remember possible next free cluster
if (setStart) allocSearchStart_ = bgnCluster + 1;
return true;
}
bool SdVolume::cacheFlush() {
#if DISABLED(SDCARD_READONLY)
if (cacheDirty_) {
if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data))
return false;
// mirror FAT tables
if (cacheMirrorBlock_) {
if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data))
return false;
cacheMirrorBlock_ = 0;
}
cacheDirty_ = 0;
}
#endif
return true;
}
bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) {
if (cacheBlockNumber_ != blockNumber) {
if (!cacheFlush()) return false;
if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false;
cacheBlockNumber_ = blockNumber;
}
if (dirty) cacheDirty_ = true;
return true;
}
// return the size in bytes of a cluster chain
bool SdVolume::chainSize(uint32_t cluster, uint32_t *size) {
uint32_t s = 0;
do {
if (!fatGet(cluster, &cluster)) return false;
s += 512UL << clusterSizeShift_;
} while (!isEOC(cluster));
*size = s;
return true;
}
// Fetch a FAT entry
bool SdVolume::fatGet(uint32_t cluster, uint32_t *value) {
uint32_t lba;
if (cluster > (clusterCount_ + 1)) return false;
if (FAT12_SUPPORT && fatType_ == 12) {
uint16_t index = cluster;
index += index >> 1;
lba = fatStartBlock_ + (index >> 9);
if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false;
index &= 0x1FF;
uint16_t tmp = cacheBuffer_.data[index];
index++;
if (index == 512) {
if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) return false;
index = 0;
}
tmp |= cacheBuffer_.data[index] << 8;
*value = cluster & 1 ? tmp >> 4 : tmp & 0xFFF;
return true;
}
if (fatType_ == 16)
lba = fatStartBlock_ + (cluster >> 8);
else if (fatType_ == 32)
lba = fatStartBlock_ + (cluster >> 7);
else
return false;
if (lba != cacheBlockNumber_ && !cacheRawBlock(lba, CACHE_FOR_READ))
return false;
*value = (fatType_ == 16) ? cacheBuffer_.fat16[cluster & 0xFF] : (cacheBuffer_.fat32[cluster & 0x7F] & FAT32MASK);
return true;
}
// Store a FAT entry
bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
if (ENABLED(SDCARD_READONLY)) return false;
uint32_t lba;
// error if reserved cluster
if (cluster < 2) return false;
// error if not in FAT
if (cluster > (clusterCount_ + 1)) return false;
if (FAT12_SUPPORT && fatType_ == 12) {
uint16_t index = cluster;
index += index >> 1;
lba = fatStartBlock_ + (index >> 9);
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false;
// mirror second FAT
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
index &= 0x1FF;
uint8_t tmp = value;
if (cluster & 1) {
tmp = (cacheBuffer_.data[index] & 0xF) | tmp << 4;
}
cacheBuffer_.data[index] = tmp;
index++;
if (index == 512) {
lba++;
index = 0;
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false;
// mirror second FAT
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
}
tmp = value >> 4;
if (!(cluster & 1)) {
tmp = ((cacheBuffer_.data[index] & 0xF0)) | tmp >> 4;
}
cacheBuffer_.data[index] = tmp;
return true;
}
if (fatType_ == 16)
lba = fatStartBlock_ + (cluster >> 8);
else if (fatType_ == 32)
lba = fatStartBlock_ + (cluster >> 7);
else
return false;
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false;
// store entry
if (fatType_ == 16)
cacheBuffer_.fat16[cluster & 0xFF] = value;
else
cacheBuffer_.fat32[cluster & 0x7F] = value;
// mirror second FAT
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
return true;
}
// free a cluster chain
bool SdVolume::freeChain(uint32_t cluster) {
// clear free cluster location
allocSearchStart_ = 2;
do {
uint32_t next;
if (!fatGet(cluster, &next)) return false;
// free cluster
if (!fatPut(cluster, 0)) return false;
cluster = next;
} while (!isEOC(cluster));
return true;
}
/** Volume free space in clusters.
*
* \return Count of free clusters for success or -1 if an error occurs.
*/
int32_t SdVolume::freeClusterCount() {
uint32_t free = 0;
uint16_t n;
uint32_t todo = clusterCount_ + 2;
if (fatType_ == 16)
n = 256;
else if (fatType_ == 32)
n = 128;
else // put FAT12 here
return -1;
for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) {
if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1;
NOMORE(n, todo);
if (fatType_ == 16) {
for (uint16_t i = 0; i < n; i++)
if (cacheBuffer_.fat16[i] == 0) free++;
}
else {
for (uint16_t i = 0; i < n; i++)
if (cacheBuffer_.fat32[i] == 0) free++;
}
#ifdef ESP32
// Needed to reset the idle task watchdog timer on ESP32 as reading the complete FAT may easily
// block for 10+ seconds. yield() is insufficient since it blocks lower prio tasks (e.g., idle).
static millis_t nextTaskTime = 0;
const millis_t ms = millis();
if (ELAPSED(ms, nextTaskTime)) {
vTaskDelay(1); // delay 1 tick (Minimum. Usually 10 or 1 ms depending on skdconfig.h)
nextTaskTime = ms + 1000; // tickle the task manager again in 1 second
}
#endif // ESP32
}
return free;
}
/** Initialize a FAT volume.
*
* \param[in] dev The SD card where the volume is located.
*
* \param[in] part The partition to be used. Legal values for \a part are
* 1-4 to use the corresponding partition on a device formatted with
* a MBR, Master Boot Record, or zero if the device is formatted as
* a super floppy with the FAT boot sector in block zero.
*
* \return true for success, false for failure.
* Reasons for failure include not finding a valid partition, not finding a valid
* FAT file system in the specified partition or an I/O error.
*/
bool SdVolume::init(DiskIODriver* dev, uint8_t part) {
uint32_t totalBlocks, volumeStartBlock = 0;
fat32_boot_t *fbs;
sdCard_ = dev;
fatType_ = 0;
allocSearchStart_ = 2;
cacheDirty_ = 0; // cacheFlush() will write block if true
cacheMirrorBlock_ = 0;
cacheBlockNumber_ = 0xFFFFFFFF;
// if part == 0 assume super floppy with FAT boot sector in block zero
// if part > 0 assume mbr volume with partition table
if (part) {
if (part > 4) return false;
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
part_t *p = &cacheBuffer_.mbr.part[part - 1];
if ((p->boot & 0x7F) != 0 || p->totalSectors < 100 || p->firstSector == 0)
return false; // not a valid partition
volumeStartBlock = p->firstSector;
}
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
fbs = &cacheBuffer_.fbs32;
if (fbs->bytesPerSector != 512 ||
fbs->fatCount == 0 ||
fbs->reservedSectorCount == 0 ||
fbs->sectorsPerCluster == 0) {
// not valid FAT volume
return false;
}
fatCount_ = fbs->fatCount;
blocksPerCluster_ = fbs->sectorsPerCluster;
// determine shift that is same as multiply by blocksPerCluster_
clusterSizeShift_ = 0;
while (blocksPerCluster_ != _BV(clusterSizeShift_)) {
// error if not power of 2
if (clusterSizeShift_++ > 7) return false;
}
blocksPerFat_ = fbs->sectorsPerFat16 ?
fbs->sectorsPerFat16 : fbs->sectorsPerFat32;
fatStartBlock_ = volumeStartBlock + fbs->reservedSectorCount;
// count for FAT16 zero for FAT32
rootDirEntryCount_ = fbs->rootDirEntryCount;
// directory start for FAT16 dataStart for FAT32
rootDirStart_ = fatStartBlock_ + fbs->fatCount * blocksPerFat_;
// data start for FAT16 and FAT32
dataStartBlock_ = rootDirStart_ + ((32 * fbs->rootDirEntryCount + 511) / 512);
// total blocks for FAT16 or FAT32
totalBlocks = fbs->totalSectors16 ?
fbs->totalSectors16 : fbs->totalSectors32;
// total data blocks
clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
// divide by cluster size to get cluster count
clusterCount_ >>= clusterSizeShift_;
// FAT type is determined by cluster count
if (clusterCount_ < 4085) {
fatType_ = 12;
if (!FAT12_SUPPORT) return false;
}
else if (clusterCount_ < 65525)
fatType_ = 16;
else {
rootDirStart_ = fbs->fat32RootCluster;
fatType_ = 32;
}
return true;
}
#endif // SDSUPPORT

View File

@ -0,0 +1,201 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* sd/SdVolume.h
*
* Arduino SdFat Library
* Copyright (c) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include <stdint.h>
#include "../inc/MarlinConfigPre.h"
#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
#include "usb_flashdrive/Sd2Card_FlashDrive.h"
#endif
#if NEED_SD2CARD_SDIO
#include "Sd2Card_sdio.h"
#elif NEED_SD2CARD_SPI
#include "Sd2Card.h"
#endif
#include "SdFatConfig.h"
#include "SdFatStructs.h"
//==============================================================================
// SdVolume class
/**
* \brief Cache for an SD data block
*/
union cache_t {
uint8_t data[512]; // Used to access cached file data blocks.
uint16_t fat16[256]; // Used to access cached FAT16 entries.
uint32_t fat32[128]; // Used to access cached FAT32 entries.
dir_t dir[16]; // Used to access cached directory entries.
mbr_t mbr; // Used to access a cached Master Boot Record.
fat_boot_t fbs; // Used to access to a cached FAT boot sector.
fat32_boot_t fbs32; // Used to access to a cached FAT32 boot sector.
fat32_fsinfo_t fsinfo; // Used to access to a cached FAT32 FSINFO sector.
};
/**
* \class SdVolume
* \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
*/
class SdVolume {
public:
// Create an instance of SdVolume
SdVolume() : fatType_(0) {}
/**
* Clear the cache and returns a pointer to the cache. Used by the WaveRP
* recorder to do raw write to the SD card. Not for normal apps.
* \return A pointer to the cache buffer or zero if an error occurs.
*/
cache_t* cacheClear() {
if (!cacheFlush()) return 0;
cacheBlockNumber_ = 0xFFFFFFFF;
return &cacheBuffer_;
}
/**
* Initialize a FAT volume. Try partition one first then try super
* floppy format.
*
* \param[in] dev The DiskIODriver where the volume is located.
*
* \return true for success, false for failure.
* Reasons for failure include not finding a valid partition, not finding
* a valid FAT file system or an I/O error.
*/
bool init(DiskIODriver *dev) { return init(dev, 1) || init(dev, 0); }
bool init(DiskIODriver *dev, uint8_t part);
// inline functions that return volume info
uint8_t blocksPerCluster() const { return blocksPerCluster_; } //> \return The volume's cluster size in blocks.
uint32_t blocksPerFat() const { return blocksPerFat_; } //> \return The number of blocks in one FAT.
uint32_t clusterCount() const { return clusterCount_; } //> \return The total number of clusters in the volume.
uint8_t clusterSizeShift() const { return clusterSizeShift_; } //> \return The shift count required to multiply by blocksPerCluster.
uint32_t dataStartBlock() const { return dataStartBlock_; } //> \return The logical block number for the start of file data.
uint8_t fatCount() const { return fatCount_; } //> \return The number of FAT structures on the volume.
uint32_t fatStartBlock() const { return fatStartBlock_; } //> \return The logical block number for the start of the first FAT.
uint8_t fatType() const { return fatType_; } //> \return The FAT type of the volume. Values are 12, 16 or 32.
int32_t freeClusterCount();
uint32_t rootDirEntryCount() const { return rootDirEntryCount_; } /** \return The number of entries in the root directory for FAT16 volumes. */
/**
* \return The logical block number for the start of the root directory
* on FAT16 volumes or the first cluster number on FAT32 volumes.
*/
uint32_t rootDirStart() const { return rootDirStart_; }
/**
* DiskIODriver object for this volume
* \return pointer to DiskIODriver object.
*/
DiskIODriver* sdCard() { return sdCard_; }
/**
* Debug access to FAT table
*
* \param[in] n cluster number.
* \param[out] v value of entry
* \return true for success or false for failure
*/
bool dbgFat(uint32_t n, uint32_t *v) { return fatGet(n, v); }
private:
// Allow SdBaseFile access to SdVolume private data.
friend class SdBaseFile;
// value for dirty argument in cacheRawBlock to indicate read from cache
static bool const CACHE_FOR_READ = false;
// value for dirty argument in cacheRawBlock to indicate write to cache
static bool const CACHE_FOR_WRITE = true;
#if USE_MULTIPLE_CARDS
cache_t cacheBuffer_; // 512 byte cache for device blocks
uint32_t cacheBlockNumber_; // Logical number of block in the cache
DiskIODriver *sdCard_; // DiskIODriver object for cache
bool cacheDirty_; // cacheFlush() will write block if true
uint32_t cacheMirrorBlock_; // block number for mirror FAT
#else
static cache_t cacheBuffer_; // 512 byte cache for device blocks
static uint32_t cacheBlockNumber_; // Logical number of block in the cache
static DiskIODriver *sdCard_; // DiskIODriver object for cache
static bool cacheDirty_; // cacheFlush() will write block if true
static uint32_t cacheMirrorBlock_; // block number for mirror FAT
#endif
uint32_t allocSearchStart_; // start cluster for alloc search
uint8_t blocksPerCluster_; // cluster size in blocks
uint32_t blocksPerFat_; // FAT size in blocks
uint32_t clusterCount_; // clusters in one FAT
uint8_t clusterSizeShift_; // shift to convert cluster count to block count
uint32_t dataStartBlock_; // first data block number
uint8_t fatCount_; // number of FATs on volume
uint32_t fatStartBlock_; // start block for first FAT
uint8_t fatType_; // volume type (12, 16, OR 32)
uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir
uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32
bool allocContiguous(uint32_t count, uint32_t *curCluster);
uint8_t blockOfCluster(uint32_t position) const { return (position >> 9) & (blocksPerCluster_ - 1); }
uint32_t clusterStartBlock(uint32_t cluster) const { return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_); }
uint32_t blockNumber(uint32_t cluster, uint32_t position) const { return clusterStartBlock(cluster) + blockOfCluster(position); }
cache_t* cache() { return &cacheBuffer_; }
uint32_t cacheBlockNumber() const { return cacheBlockNumber_; }
#if USE_MULTIPLE_CARDS
bool cacheFlush();
bool cacheRawBlock(uint32_t blockNumber, bool dirty);
#else
static bool cacheFlush();
static bool cacheRawBlock(uint32_t blockNumber, bool dirty);
#endif
// used by SdBaseFile write to assign cache to SD location
void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) {
cacheDirty_ = dirty;
cacheBlockNumber_ = blockNumber;
}
void cacheSetDirty() { cacheDirty_ |= CACHE_FOR_WRITE; }
bool chainSize(uint32_t beginCluster, uint32_t *size);
bool fatGet(uint32_t cluster, uint32_t *value);
bool fatPut(uint32_t cluster, uint32_t value);
bool fatPutEOC(uint32_t cluster) { return fatPut(cluster, 0x0FFFFFFF); }
bool freeChain(uint32_t cluster);
bool isEOC(uint32_t cluster) const {
if (FAT12_SUPPORT && fatType_ == 12) return cluster >= FAT12EOC_MIN;
if (fatType_ == 16) return cluster >= FAT16EOC_MIN;
return cluster >= FAT32EOC_MIN;
}
bool readBlock(uint32_t block, uint8_t *dst) { return sdCard_->readBlock(block, dst); }
bool writeBlock(uint32_t block, const uint8_t *dst) { return sdCard_->writeBlock(block, dst); }
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,365 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "../inc/MarlinConfig.h"
#if ENABLED(SDSUPPORT)
extern const char M23_STR[], M24_STR[];
#if BOTH(SDCARD_SORT_ALPHA, SDSORT_DYNAMIC_RAM)
#define SD_RESORT 1
#endif
#if ENABLED(SDCARD_RATHERRECENTFIRST) && DISABLED(SDCARD_SORT_ALPHA)
#define SD_ORDER(N,C) ((C) - 1 - (N))
#else
#define SD_ORDER(N,C) N
#endif
#define MAX_DIR_DEPTH 10 // Maximum folder depth
#define MAXDIRNAMELENGTH 8 // DOS folder name size
#define MAXPATHNAMELENGTH (1 + (MAXDIRNAMELENGTH + 1) * (MAX_DIR_DEPTH) + 1 + FILENAME_LENGTH) // "/" + N * ("ADIRNAME/") + "filename.ext"
#include "SdFile.h"
#include "disk_io_driver.h"
#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
#include "usb_flashdrive/Sd2Card_FlashDrive.h"
#endif
#if NEED_SD2CARD_SDIO
#include "Sd2Card_sdio.h"
#elif NEED_SD2CARD_SPI
#include "Sd2Card.h"
#endif
#if ENABLED(MULTI_VOLUME)
#define SV_SD_ONBOARD 1
#define SV_USB_FLASH_DRIVE 2
#define _VOLUME_ID(N) _CAT(SV_, N)
#define SHARED_VOLUME_IS(N) (DEFAULT_SHARED_VOLUME == _VOLUME_ID(N))
#if !SHARED_VOLUME_IS(SD_ONBOARD) && !SHARED_VOLUME_IS(USB_FLASH_DRIVE)
#error "DEFAULT_SHARED_VOLUME must be either SD_ONBOARD or USB_FLASH_DRIVE."
#endif
#else
#define SHARED_VOLUME_IS(...) 0
#endif
typedef struct {
bool saving:1,
logging:1,
sdprinting:1,
sdprintdone:1,
mounted:1,
filenameIsDir:1,
workDirIsRoot:1,
abort_sd_printing:1
#if ENABLED(BINARY_FILE_TRANSFER)
, binary_mode:1
#endif
;
} card_flags_t;
#if ENABLED(AUTO_REPORT_SD_STATUS)
#include "../libs/autoreport.h"
#endif
class CardReader {
public:
static card_flags_t flag; // Flags (above)
static char filename[FILENAME_LENGTH], // DOS 8.3 filename of the selected item
longFilename[LONG_FILENAME_LENGTH]; // Long name of the selected item
// Fast! binary file transfer
#if ENABLED(BINARY_FILE_TRANSFER)
#if HAS_MULTI_SERIAL
static serial_index_t transfer_port_index;
#else
static constexpr serial_index_t transfer_port_index = 0;
#endif
#endif
// // // Methods // // //
CardReader();
static void changeMedia(DiskIODriver *_driver) { driver = _driver; }
static SdFile getroot() { return root; }
static void mount();
static void release();
static inline bool isMounted() { return flag.mounted; }
// Handle media insert/remove
static void manage_media();
// SD Card Logging
static void openLogFile(const char * const path);
static void write_command(char * const buf);
#if DISABLED(NO_SD_AUTOSTART) // Auto-Start auto#.g file handling
static uint8_t autofile_index; // Next auto#.g index to run, plus one. Ignored by autofile_check when zero.
static void autofile_begin(); // Begin check. Called automatically after boot-up.
static bool autofile_check(); // Check for the next auto-start file and run it.
static inline void autofile_cancel() { autofile_index = 0; }
#endif
// Basic file ops
static void openFileRead(const char * const path, const uint8_t subcall=0);
static void openFileWrite(const char * const path);
static void closefile(const bool store_location=false);
static bool fileExists(const char * const name);
static void removeFile(const char * const name);
static inline char* longest_filename() { return longFilename[0] ? longFilename : filename; }
#if ENABLED(LONG_FILENAME_HOST_SUPPORT)
static void printLongPath(char * const path); // Used by M33
#endif
// Working Directory for SD card menu
static void cdroot();
static void cd(const char *relpath);
static int8_t cdup();
static uint16_t countFilesInWorkDir();
static uint16_t get_num_Files();
// Select a file
static void selectFileByIndex(const uint16_t nr);
static void selectFileByName(const char * const match); // (working directory only)
// Print job
static void report_status();
static void getAbsFilenameInCWD(char *dst);
static void printSelectedFilename();
static void openAndPrintFile(const char *name); // (working directory or full path)
static void startOrResumeFilePrinting();
static void endFilePrintNow(TERN_(SD_RESORT, const bool re_sort=false));
static void abortFilePrintNow(TERN_(SD_RESORT, const bool re_sort=false));
static void fileHasFinished();
static inline void abortFilePrintSoon() { flag.abort_sd_printing = true; }
static inline void pauseSDPrint() { flag.sdprinting = false; }
static inline bool isPrinting() { return flag.sdprinting; }
static inline bool isPaused() { return isFileOpen() && !isPrinting(); }
#if HAS_PRINT_PROGRESS_PERMYRIAD
static inline uint16_t permyriadDone() {
if (flag.sdprintdone) return 10000;
if (isFileOpen() && filesize) return sdpos / ((filesize + 9999) / 10000);
return 0;
}
#endif
static inline uint8_t percentDone() {
if (flag.sdprintdone) return 100;
if (isFileOpen() && filesize) return sdpos / ((filesize + 99) / 100);
return 0;
}
/**
* Dive down to a relative or absolute path.
* Relative paths apply to the workDir.
*
* update_cwd: Pass 'true' to update the workDir on success.
* inDirPtr: On exit your pointer points to the target SdFile.
* A nullptr indicates failure.
* path: Start with '/' for abs path. End with '/' to get a folder ref.
* echo: Set 'true' to print the path throughout the loop.
*/
static const char* diveToFile(const bool update_cwd, SdFile* &inDirPtr, const char * const path, const bool echo=false);
#if ENABLED(SDCARD_SORT_ALPHA)
static void presort();
static void getfilename_sorted(const uint16_t nr);
#if ENABLED(SDSORT_GCODE)
FORCE_INLINE static void setSortOn(bool b) { sort_alpha = b; presort(); }
FORCE_INLINE static void setSortFolders(int i) { sort_folders = i; presort(); }
//FORCE_INLINE static void setSortReverse(bool b) { sort_reverse = b; }
#endif
#else
FORCE_INLINE static void getfilename_sorted(const uint16_t nr) { selectFileByIndex(nr); }
#endif
static void ls();
#if ENABLED(POWER_LOSS_RECOVERY)
static bool jobRecoverFileExists();
static void openJobRecoveryFile(const bool read);
static void removeJobRecoveryFile();
#endif
// Current Working Dir - Set by cd, cdup, cdroot, and diveToFile(true, ...)
static inline char* getWorkDirName() { workDir.getDosName(filename); return filename; }
static inline SdFile& getWorkDir() { return workDir.isOpen() ? workDir : root; }
// Print File stats
static inline uint32_t getFileSize() { return filesize; }
static inline uint32_t getIndex() { return sdpos; }
static inline bool isFileOpen() { return isMounted() && file.isOpen(); }
static inline bool eof() { return getIndex() >= getFileSize(); }
// File data operations
static inline int16_t get() { int16_t out = (int16_t)file.read(); sdpos = file.curPosition(); return out; }
static inline int16_t read(void *buf, uint16_t nbyte) { return file.isOpen() ? file.read(buf, nbyte) : -1; }
static inline int16_t write(void *buf, uint16_t nbyte) { return file.isOpen() ? file.write(buf, nbyte) : -1; }
static inline void setIndex(const uint32_t index) { file.seekSet((sdpos = index)); }
// TODO: rename to diskIODriver()
static DiskIODriver* diskIODriver() { return driver; }
#if ENABLED(AUTO_REPORT_SD_STATUS)
//
// SD Auto Reporting
//
struct AutoReportSD { static void report() { report_status(); } };
static AutoReporter<AutoReportSD> auto_reporter;
#endif
#if SHARED_VOLUME_IS(USB_FLASH_DRIVE) || ENABLED(USB_FLASH_DRIVE_SUPPORT)
#define HAS_USB_FLASH_DRIVE 1
static DiskIODriver_USBFlash media_driver_usbFlash;
#endif
#if NEED_SD2CARD_SDIO || NEED_SD2CARD_SPI
typedef TERN(NEED_SD2CARD_SDIO, DiskIODriver_SDIO, DiskIODriver_SPI_SD) sdcard_driver_t;
static sdcard_driver_t media_driver_sdcard;
#endif
private:
//
// Working directory and parents
//
static SdFile root, workDir, workDirParents[MAX_DIR_DEPTH];
static uint8_t workDirDepth;
//
// Alphabetical file and folder sorting
//
#if ENABLED(SDCARD_SORT_ALPHA)
static uint16_t sort_count; // Count of sorted items in the current directory
#if ENABLED(SDSORT_GCODE)
static bool sort_alpha; // Flag to enable / disable the feature
static int sort_folders; // Folder sorting before/none/after
//static bool sort_reverse; // Flag to enable / disable reverse sorting
#endif
// By default the sort index is static
#if ENABLED(SDSORT_DYNAMIC_RAM)
static uint8_t *sort_order;
#else
static uint8_t sort_order[SDSORT_LIMIT];
#endif
#if BOTH(SDSORT_USES_RAM, SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM)
#define SORTED_LONGNAME_MAXLEN (SDSORT_CACHE_VFATS) * (FILENAME_LENGTH)
#define SORTED_LONGNAME_STORAGE (SORTED_LONGNAME_MAXLEN + 1)
#else
#define SORTED_LONGNAME_MAXLEN LONG_FILENAME_LENGTH
#define SORTED_LONGNAME_STORAGE SORTED_LONGNAME_MAXLEN
#endif
// Cache filenames to speed up SD menus.
#if ENABLED(SDSORT_USES_RAM)
// If using dynamic ram for names, allocate on the heap.
#if ENABLED(SDSORT_CACHE_NAMES)
static uint16_t nrFiles; // Cache the total count
#if ENABLED(SDSORT_DYNAMIC_RAM)
static char **sortshort, **sortnames;
#else
static char sortshort[SDSORT_LIMIT][FILENAME_LENGTH];
#endif
#endif
#if (ENABLED(SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM)) || NONE(SDSORT_CACHE_NAMES, SDSORT_USES_STACK)
static char sortnames[SDSORT_LIMIT][SORTED_LONGNAME_STORAGE];
#endif
// Folder sorting uses an isDir array when caching items.
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_DYNAMIC_RAM)
static uint8_t *isDir;
#elif ENABLED(SDSORT_CACHE_NAMES) || DISABLED(SDSORT_USES_STACK)
static uint8_t isDir[(SDSORT_LIMIT + 7) >> 3];
#endif
#endif
#endif // SDSORT_USES_RAM
#endif // SDCARD_SORT_ALPHA
static DiskIODriver *driver;
static SdVolume volume;
static SdFile file;
static uint32_t filesize, // Total size of the current file, in bytes
sdpos; // Index most recently read (one behind file.getPos)
//
// Procedure calls to other files
//
#if HAS_MEDIA_SUBCALLS
static uint8_t file_subcall_ctr;
static uint32_t filespos[SD_PROCEDURE_DEPTH];
static char proc_filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
#endif
//
// Directory items
//
static bool is_dir_or_gcode(const dir_t &p);
static int countItems(SdFile dir);
static void selectByIndex(SdFile dir, const uint8_t index);
static void selectByName(SdFile dir, const char * const match);
static void printListing(SdFile parent, const char * const prepend=nullptr);
#if ENABLED(SDCARD_SORT_ALPHA)
static void flush_presort();
#endif
};
#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
#define IS_SD_INSERTED() DiskIODriver_USBFlash::isInserted()
#elif PIN_EXISTS(SD_DETECT)
#define IS_SD_INSERTED() (READ(SD_DETECT_PIN) == SD_DETECT_STATE)
#else
// No card detect line? Assume the card is inserted.
#define IS_SD_INSERTED() true
#endif
#define IS_SD_PRINTING() (card.flag.sdprinting && !card.flag.abort_sd_printing)
#define IS_SD_FETCHING() (!card.flag.sdprintdone && IS_SD_PRINTING())
#define IS_SD_PAUSED() card.isPaused()
#define IS_SD_FILE_OPEN() card.isFileOpen()
extern CardReader card;
#else // !SDSUPPORT
#define IS_SD_PRINTING() false
#define IS_SD_FETCHING() false
#define IS_SD_PAUSED() false
#define IS_SD_FILE_OPEN() false
#define LONG_FILENAME_LENGTH 0
#endif // !SDSUPPORT

View File

@ -0,0 +1,67 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
/**
* DiskIO Interace
*
* Interface for low level disk io
*/
class DiskIODriver {
public:
/**
* Initialize an SD flash memory card with default clock rate and chip
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*
* \return true for success or false for failure.
*/
virtual bool init(const uint8_t sckRateID, const pin_t chipSelectPin) = 0; //TODO: only for SPI
/**
* Read a card's CSD register. The CSD contains Card-Specific Data that
* provides information regarding access to the card's contents.
*
* \param[out] csd pointer to area for returned data.
*
* \return true for success or false for failure.
*/
virtual bool readCSD(csd_t* csd) = 0;
virtual bool readStart(const uint32_t block) = 0;
virtual bool readData(uint8_t* dst) = 0;
virtual bool readStop() = 0;
virtual bool writeStart(const uint32_t block, const uint32_t) = 0;
virtual bool writeData(const uint8_t* src) = 0;
virtual bool writeStop() = 0;
virtual bool readBlock(uint32_t block, uint8_t* dst) = 0;
virtual bool writeBlock(uint32_t blockNumber, const uint8_t* src) = 0;
virtual uint32_t cardSize() = 0;
virtual bool isReady() = 0;
virtual void idle() = 0;
};

View File

@ -0,0 +1,326 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
/**
* Adjust USB_DEBUG to select debugging verbosity.
* 0 - no debug messages
* 1 - basic insertion/removal messages
* 2 - show USB state transitions
* 3 - perform block range checking
* 4 - print each block access
*/
#define USB_DEBUG 1
#define USB_STARTUP_DELAY 0
// uncomment to get 'printf' console debugging. NOT FOR UNO!
//#define HOST_DEBUG(...) {char s[255]; sprintf(s,__VA_ARGS__); SERIAL_ECHOLNPAIR("UHS:",s);}
//#define BS_HOST_DEBUG(...) {char s[255]; sprintf(s,__VA_ARGS__); SERIAL_ECHOLNPAIR("UHS:",s);}
//#define MAX_HOST_DEBUG(...) {char s[255]; sprintf(s,__VA_ARGS__); SERIAL_ECHOLNPAIR("UHS:",s);}
#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
#include "../../MarlinCore.h"
#include "../../core/serial.h"
#include "../../module/temperature.h"
#if DISABLED(USE_OTG_USB_HOST) && !PINS_EXIST(USB_CS, USB_INTR)
#error "USB_FLASH_DRIVE_SUPPORT requires USB_CS_PIN and USB_INTR_PIN to be defined."
#endif
#if ENABLED(USE_UHS3_USB)
#define NO_AUTO_SPEED
#define UHS_MAX3421E_SPD 8000000 >> SD_SPI_SPEED
#define UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE 1
#define UHS_HOST_MAX_INTERFACE_DRIVERS 2
#define MASS_MAX_SUPPORTED_LUN 1
#define USB_HOST_SERIAL MYSERIAL1
// Workaround for certain issues with UHS3
#define SKIP_PAGE3F // Required for IOGEAR media adapter
#define USB_NO_TEST_UNIT_READY // Required for removable media adapter
#define USB_HOST_MANUAL_POLL // Optimization to shut off IRQ automatically
// Workarounds for keeping Marlin's watchdog timer from barking...
void marlin_yield() {
thermalManager.manage_heater();
}
#define SYSTEM_OR_SPECIAL_YIELD(...) marlin_yield();
#define delay(x) safe_delay(x)
#define LOAD_USB_HOST_SYSTEM
#define LOAD_USB_HOST_SHIELD
#define LOAD_UHS_BULK_STORAGE
#define MARLIN_UHS_WRITE_SS(v) WRITE(USB_CS_PIN, v)
#define MARLIN_UHS_READ_IRQ() READ(USB_INTR_PIN)
#include "lib-uhs3/UHS_host/UHS_host.h"
MAX3421E_HOST usb(USB_CS_PIN, USB_INTR_PIN);
UHS_Bulk_Storage bulk(&usb);
#define UHS_START (usb.Init() == 0)
#define UHS_STATE(state) UHS_USB_HOST_STATE_##state
#elif ENABLED(USE_OTG_USB_HOST)
#if HAS_SD_HOST_DRIVE
#include HAL_PATH(../../HAL, msc_sd.h)
#endif
#include HAL_PATH(../../HAL, usb_host.h)
#define UHS_START usb.start()
#define rREVISION 0
#define UHS_STATE(state) USB_STATE_##state
#else
#include "lib-uhs2/Usb.h"
#include "lib-uhs2/masstorage.h"
USB usb;
BulkOnly bulk(&usb);
#define UHS_START usb.start()
#define UHS_STATE(state) USB_STATE_##state
#endif
#include "Sd2Card_FlashDrive.h"
#include "../../lcd/marlinui.h"
static enum {
UNINITIALIZED,
DO_STARTUP,
WAIT_FOR_DEVICE,
WAIT_FOR_LUN,
MEDIA_READY,
MEDIA_ERROR
} state;
#if USB_DEBUG >= 3
uint32_t lun0_capacity;
#endif
bool DiskIODriver_USBFlash::usbStartup() {
if (state <= DO_STARTUP) {
SERIAL_ECHOPGM("Starting USB host...");
if (!UHS_START) {
SERIAL_ECHOLNPGM(" failed.");
LCD_MESSAGEPGM(MSG_MEDIA_USB_FAILED);
return false;
}
// SPI quick test - check revision register
switch (usb.regRd(rREVISION)) {
case 0x01: SERIAL_ECHOLNPGM("rev.01 started"); break;
case 0x12: SERIAL_ECHOLNPGM("rev.02 started"); break;
case 0x13: SERIAL_ECHOLNPGM("rev.03 started"); break;
default: SERIAL_ECHOLNPGM("started. rev unknown."); break;
}
state = WAIT_FOR_DEVICE;
}
return true;
}
// The USB library needs to be called periodically to detect USB thumbdrive
// insertion and removals. Call this idle() function periodically to allow
// the USB library to monitor for such events. This function also takes care
// of initializing the USB library for the first time.
void DiskIODriver_USBFlash::idle() {
usb.Task();
const uint8_t task_state = usb.getUsbTaskState();
#if USB_DEBUG >= 2
if (state > DO_STARTUP) {
static uint8_t laststate = 232;
if (task_state != laststate) {
laststate = task_state;
#define UHS_USB_DEBUG(x) case UHS_STATE(x): SERIAL_ECHOLNPGM(#x); break
switch (task_state) {
UHS_USB_DEBUG(IDLE);
UHS_USB_DEBUG(RESET_DEVICE);
UHS_USB_DEBUG(RESET_NOT_COMPLETE);
UHS_USB_DEBUG(DEBOUNCE);
UHS_USB_DEBUG(DEBOUNCE_NOT_COMPLETE);
UHS_USB_DEBUG(WAIT_SOF);
UHS_USB_DEBUG(ERROR);
UHS_USB_DEBUG(CONFIGURING);
UHS_USB_DEBUG(CONFIGURING_DONE);
UHS_USB_DEBUG(RUNNING);
default:
SERIAL_ECHOLNPAIR("UHS_USB_HOST_STATE: ", task_state);
break;
}
}
}
#endif
static millis_t next_state_ms = millis();
#define GOTO_STATE_AFTER_DELAY(STATE, DELAY) do{ state = STATE; next_state_ms = millis() + DELAY; }while(0)
if (ELAPSED(millis(), next_state_ms)) {
GOTO_STATE_AFTER_DELAY(state, 250); // Default delay
switch (state) {
case UNINITIALIZED:
#ifndef MANUAL_USB_STARTUP
GOTO_STATE_AFTER_DELAY( DO_STARTUP, USB_STARTUP_DELAY );
#endif
break;
case DO_STARTUP: usbStartup(); break;
case WAIT_FOR_DEVICE:
if (task_state == UHS_STATE(RUNNING)) {
#if USB_DEBUG >= 1
SERIAL_ECHOLNPGM("USB device inserted");
#endif
GOTO_STATE_AFTER_DELAY( WAIT_FOR_LUN, 250 );
}
break;
case WAIT_FOR_LUN:
/* USB device is inserted, but if it is an SD card,
* adapter it may not have an SD card in it yet. */
if (bulk.LUNIsGood(0)) {
#if USB_DEBUG >= 1
SERIAL_ECHOLNPGM("LUN is good");
#endif
GOTO_STATE_AFTER_DELAY( MEDIA_READY, 100 );
}
else {
#ifdef USB_HOST_MANUAL_POLL
// Make sure we catch disconnect events
usb.busprobe();
usb.VBUS_changed();
#endif
#if USB_DEBUG >= 1
SERIAL_ECHOLNPGM("Waiting for media");
#endif
LCD_MESSAGEPGM(MSG_MEDIA_WAITING);
GOTO_STATE_AFTER_DELAY(state, 2000);
}
break;
case MEDIA_READY: break;
case MEDIA_ERROR: break;
}
if (state > WAIT_FOR_DEVICE && task_state != UHS_STATE(RUNNING)) {
// Handle device removal events
#if USB_DEBUG >= 1
SERIAL_ECHOLNPGM("USB device removed");
#endif
if (state != MEDIA_READY)
LCD_MESSAGEPGM(MSG_MEDIA_USB_REMOVED);
GOTO_STATE_AFTER_DELAY(WAIT_FOR_DEVICE, 0);
}
else if (state > WAIT_FOR_LUN && !bulk.LUNIsGood(0)) {
// Handle media removal events
#if USB_DEBUG >= 1
SERIAL_ECHOLNPGM("Media removed");
#endif
LCD_MESSAGEPGM(MSG_MEDIA_REMOVED);
GOTO_STATE_AFTER_DELAY(WAIT_FOR_DEVICE, 0);
}
else if (task_state == UHS_STATE(ERROR)) {
LCD_MESSAGEPGM(MSG_MEDIA_READ_ERROR);
GOTO_STATE_AFTER_DELAY(MEDIA_ERROR, 0);
}
}
}
// Marlin calls this function to check whether an USB drive is inserted.
// This is equivalent to polling the SD_DETECT when using SD cards.
bool DiskIODriver_USBFlash::isInserted() {
return state == MEDIA_READY;
}
bool DiskIODriver_USBFlash::isReady() {
return state > DO_STARTUP && usb.getUsbTaskState() == UHS_STATE(RUNNING);
}
// Marlin calls this to initialize an SD card once it is inserted.
bool DiskIODriver_USBFlash::init(const uint8_t, const pin_t) {
if (!isInserted()) return false;
#if USB_DEBUG >= 1
const uint32_t sectorSize = bulk.GetSectorSize(0);
if (sectorSize != 512) {
SERIAL_ECHOLNPAIR("Expecting sector size of 512. Got: ", sectorSize);
return false;
}
#endif
#if USB_DEBUG >= 3
lun0_capacity = bulk.GetCapacity(0);
SERIAL_ECHOLNPAIR("LUN Capacity (in blocks): ", lun0_capacity);
#endif
return true;
}
// Returns the capacity of the card in blocks.
uint32_t DiskIODriver_USBFlash::cardSize() {
if (!isInserted()) return false;
#if USB_DEBUG < 3
const uint32_t
#endif
lun0_capacity = bulk.GetCapacity(0);
return lun0_capacity;
}
bool DiskIODriver_USBFlash::readBlock(uint32_t block, uint8_t *dst) {
if (!isInserted()) return false;
#if USB_DEBUG >= 3
if (block >= lun0_capacity) {
SERIAL_ECHOLNPAIR("Attempt to read past end of LUN: ", block);
return false;
}
#if USB_DEBUG >= 4
SERIAL_ECHOLNPAIR("Read block ", block);
#endif
#endif
return bulk.Read(0, block, 512, 1, dst) == 0;
}
bool DiskIODriver_USBFlash::writeBlock(uint32_t block, const uint8_t *src) {
if (!isInserted()) return false;
#if USB_DEBUG >= 3
if (block >= lun0_capacity) {
SERIAL_ECHOLNPAIR("Attempt to write past end of LUN: ", block);
return false;
}
#if USB_DEBUG >= 4
SERIAL_ECHOLNPAIR("Write block ", block);
#endif
#endif
return bulk.Write(0, block, 512, 1, src) == 0;
}
#endif // USB_FLASH_DRIVE_SUPPORT

View File

@ -0,0 +1,80 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* \file
* \brief Sd2Card class for USB Flash Drive
*/
#include "../SdFatConfig.h"
#include "../SdInfo.h"
#include "../disk_io_driver.h"
#if DISABLED(USE_OTG_USB_HOST)
/**
* Define SOFTWARE_SPI to use bit-bang SPI
*/
#if EITHER(MEGA_SOFT_SPI, USE_SOFTWARE_SPI)
#define SOFTWARE_SPI
#endif
// SPI pin definitions - do not edit here - change in SdFatConfig.h
#if ENABLED(SOFTWARE_SPI)
#warning "Auto-assigning '10' as the SD_CHIP_SELECT_PIN."
#define SD_CHIP_SELECT_PIN 10 // Software SPI chip select pin for the SD
#else
// hardware pin defs
#define SD_CHIP_SELECT_PIN SD_SS_PIN // The default chip select pin for the SD card is SS.
#endif
#endif
class DiskIODriver_USBFlash : public DiskIODriver {
private:
uint32_t pos;
static void usbStateDebug();
public:
static bool usbStartup();
static bool isInserted();
bool init(const uint8_t sckRateID=0, const pin_t chipSelectPin=TERN(USE_OTG_USB_HOST, 0, SD_CHIP_SELECT_PIN)) override;
inline bool readCSD(csd_t*) override { return true; }
inline bool readStart(const uint32_t block) override { pos = block; return isReady(); }
inline bool readData(uint8_t *dst) override { return readBlock(pos++, dst); }
inline bool readStop() override { return true; }
inline bool writeStart(const uint32_t block, const uint32_t) override { pos = block; return isReady(); }
inline bool writeData(const uint8_t *src) override { return writeBlock(pos++, src); }
inline bool writeStop() override { return true; }
bool readBlock(uint32_t block, uint8_t *dst) override;
bool writeBlock(uint32_t blockNumber, const uint8_t *src) override;
uint32_t cardSize() override;
bool isReady() override;
void idle() override;
};

View File

@ -0,0 +1,46 @@
==== USB HOST SHIELD 2.0 LIBRARY ====
The lib/ folder contains a subset of the files from the USB Host Shield 2.0
library:
https://github.com/felis/USB_Host_Shield_2.0
While the original library was released under the GPLv2 and could not be
commingled with Marlin, the developers have graciously re-licenced the
files needed for Marlin as "GPLv2 or later", as documented in this thread.
https://github.com/felis/USB_Host_Shield_2.0/issues/364
Small modifications have been made to the source. Please search for
USB_FLASH_DRIVE_SUPPORT or look at the patch file to see what was changed.
==== LICENSE SUMMARY ====
Source Path: Repository: License:
------------ ----------- --------
lib-uhs3/ github.com/felis/USB_Host_Shield_2.0 GPLv2 or later
lib-uhs3/lib/masstorage.cpp github.com/greiman/UsbFat [1] MIT
lib-uhs3/lib/settings.h github.com/greiman/UsbFat [1] MIT
[1] Changes related to SKIP_WRITE_PROTECT and DELAY only
==== PERFORMANCE ENHANCEMENTS FOR USB DRIVES ====
There are also some small performance enhancements from Bill Greiman, regarding
SKIP_WRITE_PROTECT and DELAY. These changes came from the following repo:
https://github.com/greiman/UsbFat
While the original library was released under the GPLv2 and could not be
commingled with Marlin, the developer has graciously re-licenced his changes
under the "MIT" license, as documented here:
https://github.com/greiman/UsbFat/issues/8
==== MARLIN INTEGRATION WORK ====
All additional work done to integrate USB into Marlin was performed by AlephObjects, Inc.
and is licensed under the GPLv3.
-- marcio@alephobjects.com

View File

@ -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

View File

@ -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_

View File

@ -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)

View File

@ -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;
//}
};

View File

@ -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);
}

View File

@ -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;
}
}
}
}

View File

@ -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))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,559 @@
/**
* 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);
uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf OPTARG(MS_WANT_PARSER, uint8_t flags=0));
uint8_t HandleUsbError(uint8_t error, uint8_t index);
uint8_t HandleSCSIError(uint8_t status);
};

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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);
};

View File

@ -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_nybbles = sizeof (T) * 2;
do {
char v = 48 + (((val >> (num_nybbles - 1) * 4)) & 0x0F);
if (v > 57) v += 7;
E_Notifyc(v, lvl);
} while (--num_nybbles);
}
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_nybbles = sizeof (T) * 2;
do {
char v = 48 + (((val >> (num_nybbles - 1) * 4)) & 0x0F);
if (v > 57) v += 7;
USB_HOST_SERIAL.print(v);
} while (--num_nybbles);
}
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
}

View File

@ -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 MYSERIAL1
#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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,31 @@
==== USB HOST SHIELD 3.0 LIBRARY ====
The lib-uhs3/ folder contains a subset of the files from the USB Host Shield
3.0 library:
https://github.com/felis/UHS30
==== LICENSE SUMMARY ====
Source Path: Repository: License:
------------ ----------- --------
usb_flashdrive/lib github.com/felis/UHS30 GPLv2 or later
==== MARLIN INTEGRATION WORK ====
All additional work done to integrate USB into Marlin was performed by
AlephObjects, Inc. and is licensed under the GPLv3.
This version of UHS3 has been modified for better compatibility with Marlin.
The upstream version of UHS 3.0 runs a frame timer interrupt every 1 ms to
handle device polling. This timer interrupt interferes with Marlin's stepper
IRQ, so the flag USB_HOST_MANUAL_POLL has been added to move the polling to
the idle task. Additional logic was added to disable and enable the frame
IRQ.
SKIP_PAGE3F and USB_NO_TEST_UNIT_READY were added to work around bugs with
certain devices.
-- marcio@alephobjects.com

View File

@ -0,0 +1,249 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 __UHS_BULK_STORAGE_H__
#define __UHS_BULK_STORAGE_H__
////////////////////////////////////////////////////////////////////////////////
// Define any of these options at the top of your sketch to override
// the defaults contained herewith. Do NOT do modifications here.
// Macro | Settings and notes | Default
// -----------------------------------------+-----------------------+-----------
// | 1 to 8 |
// | Each LUN needs |
// MASS_MAX_SUPPORTED_LUN | ~13 bytes to be able | 8
// | to track the state of |
// | each unit. |
// -----------------------------------------+-----------------------+-----------
// | Just define to use. |
// DEBUG_PRINTF_EXTRA_HUGE_UHS_BULK_STORAGE | works only if extra |
// | huge debug is on too. |
// -----------------------------------------^-----------------------^-----------
#ifndef MASS_MAX_SUPPORTED_LUN
#define MASS_MAX_SUPPORTED_LUN 8
#endif
#include "UHS_SCSI.h"
#define UHS_BULK_bmREQ_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
#define UHS_BULK_bmREQ_IN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
// Request Codes
#define UHS_BULK_REQ_ADSC 0x00U
#define UHS_BULK_REQ_GET 0xFCU
#define UHS_BULK_REQ_PUT 0xFDU
#define UHS_BULK_REQ_GET_MAX_LUN 0xFEU
#define UHS_BULK_REQ_BOMSR 0xFFU // Mass Storage Reset
#define UHS_BULK_CBW_SIGNATURE 0x43425355LU
#define UHS_BULK_CSW_SIGNATURE 0x53425355LU
#define UHS_BULK_CMD_DIR_OUT 0x00U
#define UHS_BULK_CMD_DIR_IN 0x80U
/* Bulk error codes */
#define UHS_BULK_ERR_SUCCESS UHS_HOST_ERROR_NONE
#define UHS_BULK_ERR_PHASE_ERROR 0x22U
#define UHS_BULK_ERR_UNIT_NOT_READY 0x23U
#define UHS_BULK_ERR_UNIT_BUSY 0x24U
#define UHS_BULK_ERR_STALL 0x25U
#define UHS_BULK_ERR_CMD_NOT_SUPPORTED 0x26U
#define UHS_BULK_ERR_INVALID_CSW 0x27U
#define UHS_BULK_ERR_NO_MEDIA 0x28U
#define UHS_BULK_ERR_BAD_LBA 0x29U
#define UHS_BULK_ERR_MEDIA_CHANGED 0x2AU
#define UHS_BULK_ERR_DEVICE_DISCONNECTED UHS_HOST_ERROR_UNPLUGGED
#define UHS_BULK_ERR_UNABLE_TO_RECOVER 0x32U // Reset recovery error
#define UHS_BULK_ERR_INVALID_LUN 0x33U
#define UHS_BULK_ERR_WRITE_STALL 0x34U
#define UHS_BULK_ERR_READ_NAKS 0x35U
#define UHS_BULK_ERR_WRITE_NAKS 0x36U
#define UHS_BULK_ERR_WRITE_PROTECTED 0x37U
#define UHS_BULK_ERR_NOT_IMPLEMENTED 0xFDU
#define UHS_BULK_ERR_GENERAL_SCSI_ERROR 0xF0U
#define UHS_BULK_ERR_GENERAL_USB_ERROR 0xFFU
#define UHS_BULK_ERR_USER 0xA0U // For subclasses to define their own error codes
#define MASS_MAX_ENDPOINTS 3
struct UHS_BULK_CommandBlockWrapperBase {
volatile uint32_t dCBWSignature;
volatile uint32_t dCBWTag;
volatile uint32_t dCBWDataTransferLength;
volatile uint8_t bmCBWFlags;
public:
UHS_BULK_CommandBlockWrapperBase() {
}
UHS_BULK_CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) :
dCBWSignature(UHS_BULK_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) {
}
} __attribute__((packed));
struct UHS_BULK_CommandBlockWrapper : public UHS_BULK_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.
UHS_BULK_CommandBlockWrapper() :
UHS_BULK_CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) {
for(int i = 0; i < 16; i++) CBWCB[i] = 0;
}
// Generic Wrap, CDB zeroed.
UHS_BULK_CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) :
UHS_BULK_CommandBlockWrapperBase(tag, xflen, flgs),
bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) {
for(int i = 0; i < 16; i++) CBWCB[i] = 0;
SCSI_CDB_BASE_t *x = reinterpret_cast<SCSI_CDB_BASE_t *>(CBWCB);
x->LUN = cmd;
}
// Wrap for CDB of 6
UHS_BULK_CommandBlockWrapper(uint32_t tag, uint32_t xflen, SCSI_CDB6_t *cdb, uint8_t dir) :
UHS_BULK_CommandBlockWrapperBase(tag, xflen, dir),
bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) {
memcpy(&CBWCB, cdb, 6);
}
// Wrap for CDB of 10
UHS_BULK_CommandBlockWrapper(uint32_t tag, uint32_t xflen, SCSI_CDB10_t *cdb, uint8_t dir) :
UHS_BULK_CommandBlockWrapperBase(tag, xflen, dir),
bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) {
memcpy(&CBWCB, cdb, 10);
}
} __attribute__((packed));
struct UHS_BULK_CommandStatusWrapper {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
} __attribute__((packed));
class UHS_Bulk_Storage : public UHS_USBInterface {
protected:
static const uint8_t epDataInIndex = 1; // DataIn endpoint index
static const uint8_t epDataOutIndex = 2; // DataOUT endpoint index
static const uint8_t epInterruptInIndex = 3; // InterruptIN endpoint index
uint8_t bMaxLUN; // Max LUN
volatile uint32_t dCBWTag; // Tag
volatile uint8_t bTheLUN; // Active LUN
volatile uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors
volatile uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits
volatile bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes.
volatile bool WriteOk[MASS_MAX_SUPPORTED_LUN];
void PrintEndpointDescriptor(const USB_FD_ENDPOINT_DESCRIPTOR* ep_ptr);
public:
UHS_Bulk_Storage(UHS_USB_HOST_BASE *p);
volatile UHS_EpInfo epInfo[MASS_MAX_ENDPOINTS];
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 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);
uint8_t SCSITransaction6(SCSI_CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
uint8_t SCSITransaction10(SCSI_CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
// Configure and internal methods, these should never be called by a user's sketch.
uint8_t Start();
bool OKtoEnumerate(ENUMERATION_INFO *ei);
uint8_t SetInterface(ENUMERATION_INFO *ei);
uint8_t GetAddress() {
return bAddress;
};
void Poll();
void DriverDefaults();
private:
void Reset();
void CheckMedia();
bool IsValidCBW(uint8_t size, uint8_t *pcbw);
bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw);
bool IsValidCSW(UHS_BULK_CommandStatusWrapper *pcsw, UHS_BULK_CommandBlockWrapperBase *pcbw);
bool CheckLUN(uint8_t lun);
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);
uint8_t ResetRecovery();
uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf);
uint8_t Page3F(uint8_t lun);
uint8_t ClearEpHalt(uint8_t index);
uint8_t Transaction(UHS_BULK_CommandBlockWrapper *cbw, uint16_t bsize, void *buf);
uint8_t HandleUsbError(uint8_t error, uint8_t index);
uint8_t HandleSCSIError(uint8_t status);
};
#if defined(LOAD_UHS_BULK_STORAGE) && !defined(UHS_BULK_STORAGE_LOADED)
#include "UHS_BULK_STORAGE_INLINE.h"
#endif
#endif // __MASSTORAGE_H__

View File

@ -0,0 +1,327 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 UHS_SCSI_H
#define UHS_SCSI_H
/*
* 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 0x00U
#define SCSI_CMD_REQUEST_SENSE 0x03U
#define SCSI_CMD_FORMAT_UNIT 0x04U
#define SCSI_CMD_READ_6 0x08U
#define SCSI_CMD_WRITE_6 0x0AU
#define SCSI_CMD_INQUIRY 0x12U
#define SCSI_CMD_MODE_SELECT_6 0x15U
#define SCSI_CMD_MODE_SENSE_6 0x1AU
#define SCSI_CMD_START_STOP_UNIT 0x1BU
#define SCSI_CMD_PREVENT_REMOVAL 0x1EU
/* Group 2 Commands (CDB's here are 10-bytes) */
#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23U
#define SCSI_CMD_READ_CAPACITY_10 0x25U
#define SCSI_CMD_READ_10 0x28U
#define SCSI_CMD_WRITE_10 0x2AU
#define SCSI_CMD_SEEK_10 0x2BU
#define SCSI_CMD_ERASE_10 0x2CU
#define SCSI_CMD_WRITE_AND_VERIFY_10 0x2EU
#define SCSI_CMD_VERIFY_10 0x2FU
#define SCSI_CMD_SYNCHRONIZE_CACHE 0x35U
#define SCSI_CMD_WRITE_BUFFER 0x3BU
#define SCSI_CMD_READ_BUFFER 0x3CU
#define SCSI_CMD_READ_SUBCHANNEL 0x42U
#define SCSI_CMD_READ_TOC 0x43U
#define SCSI_CMD_READ_HEADER 0x44U
#define SCSI_CMD_PLAY_AUDIO_10 0x45U
#define SCSI_CMD_GET_CONFIGURATION 0x46U
#define SCSI_CMD_PLAY_AUDIO_MSF 0x47U
#define SCSI_CMD_PLAY_AUDIO_TI 0x48U
#define SCSI_CMD_PLAY_TRACK_REL_10 0x49U
#define SCSI_CMD_GET_EVENT_STATUS 0x4AU
#define SCSI_CMD_PAUSE_RESUME 0x4BU
#define SCSI_CMD_READ_DISC_INFORMATION 0x51U
#define SCSI_CMD_READ_TRACK_INFORMATION 0x52U
#define SCSI_CMD_RESERVE_TRACK 0x53U
#define SCSI_CMD_SEND_OPC_INFORMATION 0x54U
#define SCSI_CMD_MODE_SELECT_10 0x55U
#define SCSI_CMD_REPAIR_TRACK 0x58U
#define SCSI_CMD_MODE_SENSE_10 0x5AU
#define SCSI_CMD_CLOSE_TRACK_SESSION 0x5BU
#define SCSI_CMD_READ_BUFFER_CAPACITY 0x5CU
#define SCSI_CMD_SEND_CUE_SHEET 0x5DU
/* Group 5 Commands (CDB's here are 12-bytes) */
#define SCSI_CMD_REPORT_LUNS 0xA0U
#define SCSI_CMD_BLANK 0xA1U
#define SCSI_CMD_SECURITY_PROTOCOL_IN 0xA2U
#define SCSI_CMD_SEND_KEY 0xA3U
#define SCSI_CMD_REPORT_KEY 0xA4U
#define SCSI_CMD_PLAY_AUDIO_12 0xA5U
#define SCSI_CMD_LOAD_UNLOAD 0xA6U
#define SCSI_CMD_SET_READ_AHEAD 0xA7U
#define SCSI_CMD_READ_12 0xA8U
#define SCSI_CMD_PLAY_TRACK_REL_12 0xA9U
#define SCSI_CMD_WRITE_12 0xAAU
#define SCSI_CMD_READ_MEDIA_SERIAL_12 0xABU
#define SCSI_CMD_GET_PERFORMANCE 0xACU
#define SCSI_CMD_READ_DVD_STRUCTURE 0xADU
#define SCSI_CMD_SECURITY_PROTOCOL_OUT 0xB5U
#define SCSI_CMD_SET_STREAMING 0xB6U
#define SCSI_CMD_READ_MSF 0xB9U
#define SCSI_CMD_SET_SPEED 0xBBU
#define SCSI_CMD_MECHANISM_STATUS 0xBDU
#define SCSI_CMD_READ_CD 0xBEU
#define SCSI_CMD_SEND_DISC_STRUCTURE 0xBFU
/* Vendor-unique Commands, included for completeness */
#define SCSI_CMD_CD_PLAYBACK_STATUS 0xC4U /* SONY unique */
#define SCSI_CMD_PLAYBACK_CONTROL 0xC9U /* SONY unique */
#define SCSI_CMD_READ_CDDA 0xD8U /* Vendor unique */
#define SCSI_CMD_READ_CDXA 0xDBU /* Vendor unique */
#define SCSI_CMD_READ_ALL_SUBCODES 0xDFU /* Vendor unique */
/* SCSI error codes */
#define SCSI_S_NOT_READY 0x02U
#define SCSI_S_MEDIUM_ERROR 0x03U
#define SCSI_S_ILLEGAL_REQUEST 0x05U
#define SCSI_S_UNIT_ATTENTION 0x06U
#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21U
#define SCSI_ASC_MEDIA_CHANGED 0x28U
#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3AU
struct SCSI_Capacity {
uint8_t data[8];
//uint32_t dwBlockAddress;
//uint32_t dwBlockLength;
} __attribute__((packed));
struct SCSI_CDB_BASE {
uint8_t Opcode;
unsigned unused : 5;
unsigned LUN : 3;
uint8_t info[12];
} __attribute__((packed));
typedef SCSI_CDB_BASE SCSI_CDB_BASE_t;
struct SCSI_CDB6 {
uint8_t Opcode;
unsigned LBAMSB : 5;
unsigned LUN : 3;
uint8_t LBAHB;
uint8_t LBALB;
uint8_t AllocationLength;
uint8_t Control;
public:
SCSI_CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) :
Opcode(_Opcode), LBAMSB(UHS_UINT8_BYTE2(LBA) & 0x1F), LUN(_LUN), LBAHB(UHS_UINT8_BYTE1(LBA)), LBALB(UHS_UINT8_BYTE0(LBA)),
AllocationLength(_AllocationLength), Control(_Control) {
}
SCSI_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 SCSI_CDB6 SCSI_CDB6_t;
struct SCSI_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:
SCSI_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) {
}
SCSI_CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) :
Opcode(_Opcode), Service_Action(0), LUN(_LUN),
LBA_L_M_MB(UHS_UINT8_BYTE3(_LBA)), LBA_L_M_LB(UHS_UINT8_BYTE2(_LBA)), LBA_L_L_MB(UHS_UINT8_BYTE1(_LBA)), LBA_L_L_LB(UHS_UINT8_BYTE0(_LBA)),
Misc2(0), ALC_MB(UHS_UINT8_BYTE1(xflen)), ALC_LB(UHS_UINT8_BYTE0(xflen)), Control(0) {
}
} __attribute__((packed));
typedef SCSI_CDB10 SCSI_CDB10_t;
struct SCSI_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 SCSI_CDB12 SCSI_CDB12_t;
struct SCSI_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 SCSI_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 SCSI_Inquiry_Response {
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;
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 SCSI_Request_Sense_Response {
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));
#endif /* UHS_SCSI_H */

View File

@ -0,0 +1,33 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 _UHS_UNOFFICIAL_IDs_h
#define _UHS_UNOFFICIAL_IDs_h
// Bogus unofficial and unregistered VIDs from cloners to be listed here.
#define UHS_VID_UNOFFICIAL_JOYTECH 0x162EU // For unofficial Joytech controllers
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,336 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if !defined(_UHS_host_h_) || defined(USBCORE_H)
#error "Never include UHS_UsbCore.h directly; include UHS_Host.h instead"
#else
#define USBCORE_H
#ifndef UHS_HOST_MAX_INTERFACE_DRIVERS
#define UHS_HOST_MAX_INTERFACE_DRIVERS 0x10U // Default maximum number of USB interface drivers
#endif
#ifndef SYSTEM_OR_SPECIAL_YIELD
#define SYSTEM_OR_SPECIAL_YIELD(...) VOID0
#endif
#ifndef SYSTEM_OR_SPECIAL_YIELD_FROM_ISR
#define SYSTEM_OR_SPECIAL_YIELD_FROM_ISR(...) SYSTEM_OR_SPECIAL_YIELD
#endif
// As we make extensions to a target interface add to UHS_HOST_MAX_INTERFACE_DRIVERS
// This offset gets calculated for supporting wide subclasses, such as HID, BT, etc.
#define UHS_HID_INDEX (UHS_HOST_MAX_INTERFACE_DRIVERS + 1)
/* Common setup data constant combinations */
//get descriptor request type
#define UHS_bmREQ_GET_DESCR (USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE)
//set request type for all but 'set feature' and 'set interface'
#define UHS_bmREQ_SET (USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE)
//get interface request type
#define UHS_bmREQ_CL_GET_INTF (USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE)
// 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)
// TO-DO: Use the python script to generate these.
// TO-DO: Add _all_ subclasses here.
// USB Device Classes, Subclasses and Protocols
////////////////////////////////////////////////////////////////////////////////
// Use Class Info in the Interface Descriptors
#define UHS_USB_CLASS_USE_CLASS_INFO 0x00U
////////////////////////////////////////////////////////////////////////////////
// Audio
#define UHS_USB_CLASS_AUDIO 0x01U
// Subclasses
#define UHS_USB_SUBCLASS_AUDIOCONTROL 0x01U
#define UHS_USB_SUBCLASS_AUDIOSTREAMING 0x02U
#define UHS_USB_SUBCLASS_MIDISTREAMING 0x03U
////////////////////////////////////////////////////////////////////////////////
// Communications and CDC Control
#define UHS_USB_CLASS_COM_AND_CDC_CTRL 0x02U
////////////////////////////////////////////////////////////////////////////////
// HID
#define UHS_USB_CLASS_HID 0x03U
// Subclasses
#define UHS_HID_BOOT_SUBCLASS 0x01U
// Protocols
#define UHS_HID_PROTOCOL_HIDBOOT_KEYBOARD 0x01U
#define UHS_HID_PROTOCOL_HIDBOOT_MOUSE 0x02U
////////////////////////////////////////////////////////////////////////////////
// Physical
#define UHS_USB_CLASS_PHYSICAL 0x05U
////////////////////////////////////////////////////////////////////////////////
// Image
#define UHS_USB_CLASS_IMAGE 0x06U
////////////////////////////////////////////////////////////////////////////////
// Printer
#define UHS_USB_CLASS_PRINTER 0x07U
////////////////////////////////////////////////////////////////////////////////
// Mass Storage
#define UHS_USB_CLASS_MASS_STORAGE 0x08
// Subclasses
#define UHS_BULK_SUBCLASS_SCSI_NOT_REPORTED 0x00U // De facto use
#define UHS_BULK_SUBCLASS_RBC 0x01U
#define UHS_BULK_SUBCLASS_ATAPI 0x02U // MMC-5 (ATAPI)
#define UHS_BULK_SUBCLASS_OBSOLETE1 0x03U // Was QIC-157
#define UHS_BULK_SUBCLASS_UFI 0x04U // Specifies how to interface Floppy Disk Drives to USB
#define UHS_BULK_SUBCLASS_OBSOLETE2 0x05U // Was SFF-8070i
#define UHS_BULK_SUBCLASS_SCSI 0x06U // SCSI Transparent Command Set
#define UHS_BULK_SUBCLASS_LSDFS 0x07U // Specifies how host has to negotiate access before trying SCSI
#define UHS_BULK_SUBCLASS_IEEE1667 0x08U
// Protocols
#define UHS_STOR_PROTO_CBI 0x00U // CBI (with command completion interrupt)
#define UHS_STOR_PROTO_CBI_NO_INT 0x01U // CBI (without command completion interrupt)
#define UHS_STOR_PROTO_OBSOLETE 0x02U
#define UHS_STOR_PROTO_BBB 0x50U // Bulk Only Transport
#define UHS_STOR_PROTO_UAS 0x62U
////////////////////////////////////////////////////////////////////////////////
// Hub
#define UHS_USB_CLASS_HUB 0x09U
////////////////////////////////////////////////////////////////////////////////
// CDC-Data
#define UHS_USB_CLASS_CDC_DATA 0x0AU
////////////////////////////////////////////////////////////////////////////////
// Smart-Card
#define UHS_USB_CLASS_SMART_CARD 0x0BU
////////////////////////////////////////////////////////////////////////////////
// Content Security
#define UHS_USB_CLASS_CONTENT_SECURITY 0x0DU
////////////////////////////////////////////////////////////////////////////////
// Video
#define UHS_USB_CLASS_VIDEO 0x0EU
////////////////////////////////////////////////////////////////////////////////
// Personal Healthcare
#define UHS_USB_CLASS_PERSONAL_HEALTH 0x0FU
////////////////////////////////////////////////////////////////////////////////
// Diagnostic Device
#define UHS_USB_CLASS_DIAGNOSTIC_DEVICE 0xDCU
////////////////////////////////////////////////////////////////////////////////
// Wireless Controller
#define UHS_USB_CLASS_WIRELESS_CTRL 0xE0U
////////////////////////////////////////////////////////////////////////////////
// Miscellaneous
#define UHS_USB_CLASS_MISC 0xEFU
////////////////////////////////////////////////////////////////////////////////
// Application Specific
#define UHS_USB_CLASS_APP_SPECIFIC 0xFEU
////////////////////////////////////////////////////////////////////////////////
// Vendor Specific
#define UHS_USB_CLASS_VENDOR_SPECIFIC 0xFFU
////////////////////////////////////////////////////////////////////////////////
/* USB state machine states */
#define UHS_USB_HOST_STATE_MASK 0xF0U
// Configure states, MSN == 0 --------------------------V
#define UHS_USB_HOST_STATE_DETACHED 0x00U
#define UHS_USB_HOST_STATE_DEBOUNCE 0x01U
#define UHS_USB_HOST_STATE_DEBOUNCE_NOT_COMPLETE 0x02U
#define UHS_USB_HOST_STATE_RESET_NOT_COMPLETE 0x03U
#define UHS_USB_HOST_STATE_WAIT_SOF 0x04U
#define UHS_USB_HOST_STATE_WAIT_BUS_READY 0x05U
#define UHS_USB_HOST_STATE_RESET_DEVICE 0x0AU
#define UHS_USB_HOST_STATE_CONFIGURING 0x0CU // Looks like "CO"nfig (backwards)
#define UHS_USB_HOST_STATE_CONFIGURING_DONE 0x0DU // Looks like "DO"one (backwards)
#define UHS_USB_HOST_STATE_CHECK 0x0EU
#define UHS_USB_HOST_STATE_ILLEGAL 0x0FU // Foo
// Run states, MSN != 0 --------------------------------V
#define UHS_USB_HOST_STATE_RUNNING 0x60U // Looks like "GO"
#define UHS_USB_HOST_STATE_IDLE 0x1DU // Looks like "ID"le
#define UHS_USB_HOST_STATE_ERROR 0xF0U // Looks like "FO"o
#define UHS_USB_HOST_STATE_INITIALIZE 0x10U // Looks like "I"nit
// Host SE result codes.
// Common SE results are stored in the low nybble, all interface drivers understand these plus 0x1F.
// Extended SE results are 0x10-0x1E. SE code only understands these internal to the hardware.
// Values > 0x1F are driver or other internal error conditions.
// Return these result codes from your host controller driver to match the error condition
// ALL Non-zero values are errors.
// Values not listed in this table are not handled in the base class, or any host driver.
#define UHS_HOST_ERROR_NONE 0x00U // No error
#define UHS_HOST_ERROR_BUSY 0x01U // transfer pending
#define UHS_HOST_ERROR_BADREQ 0x02U // Transfer Launch Request was bad
#define UHS_HOST_ERROR_DMA 0x03U // DMA was too short, or too long
#define UHS_HOST_ERROR_NAK 0x04U // Peripheral returned NAK
#define UHS_HOST_ERROR_STALL 0x05U // Peripheral returned STALL
#define UHS_HOST_ERROR_TOGERR 0x06U // Toggle error/ISO over-underrun
#define UHS_HOST_ERROR_WRONGPID 0x07U // Received wrong Packet ID
#define UHS_HOST_ERROR_BADBC 0x08U // Byte count is bad
#define UHS_HOST_ERROR_PIDERR 0x09U // Received Packet ID is corrupted
#define UHS_HOST_ERROR_BADRQ 0x0AU // Packet error. Increase max packet.
#define UHS_HOST_ERROR_CRC 0x0BU // USB CRC was incorrect
#define UHS_HOST_ERROR_KERR 0x0CU // K-state instead of response, usually indicates wrong speed
#define UHS_HOST_ERROR_JERR 0x0DU // J-state instead of response, usually indicates wrong speed
#define UHS_HOST_ERROR_TIMEOUT 0x0EU // Device did not respond in time
#define UHS_HOST_ERROR_BABBLE 0x0FU // Line noise/unexpected data
#define UHS_HOST_ERROR_MEM_LAT 0x10U // Error caused by memory latency.
#define UHS_HOST_ERROR_NYET 0x11U // OUT transfer accepted with NYET
// Addressing error codes
#define ADDR_ERROR_INVALID_INDEX 0xA0U
#define ADDR_ERROR_INVALID_ADDRESS 0xA1U
// Common Interface Driver error codes
#define UHS_HOST_ERROR_DEVICE_NOT_SUPPORTED 0xD1U // Driver doesn't support the device or interfaces
#define UHS_HOST_ERROR_DEVICE_INIT_INCOMPLETE 0xD2U // Init partially finished, but died.
#define UHS_HOST_ERROR_CANT_REGISTER_DEVICE_CLASS 0xD3U // There was no driver for the interface requested.
#define UHS_HOST_ERROR_ADDRESS_POOL_FULL 0xD4U // No addresses left in the address pool.
#define UHS_HOST_ERROR_HUB_ADDRESS_OVERFLOW 0xD5U // No hub addresses left. The maximum is 7.
#define UHS_HOST_ERROR_NO_ADDRESS_IN_POOL 0xD6U // Address was not allocated in the pool, thus not found.
#define UHS_HOST_ERROR_NULL_EPINFO 0xD7U // The supplied endpoint was NULL, indicates a bug or other problem.
#define UHS_HOST_ERROR_BAD_ARGUMENT 0xD8U // Indicates a range violation bug.
#define UHS_HOST_ERROR_DEVICE_DRIVER_BUSY 0xD9U // The interface driver is busy or out buffer is full, try again later.
#define UHS_HOST_ERROR_BAD_MAX_PACKET_SIZE 0xDAU // The maximum packet size was exceeded. Try again with smaller size.
#define UHS_HOST_ERROR_NO_ENDPOINT_IN_TABLE 0xDBU // The endpoint could not be found in the endpoint table.
#define UHS_HOST_ERROR_UNPLUGGED 0xDEU // Someone removed the USB device, or Vbus was turned off.
#define UHS_HOST_ERROR_NOMEM 0xDFU // Out Of Memory.
// Control request stream errors
#define UHS_HOST_ERROR_FailGetDevDescr 0xE1U
#define UHS_HOST_ERROR_FailSetDevTblEntry 0xE2U
#define UHS_HOST_ERROR_FailGetConfDescr 0xE3U
#define UHS_HOST_ERROR_END_OF_STREAM 0xEFU
// Host base class specific Error codes
#define UHS_HOST_ERROR_NOT_IMPLEMENTED 0xFEU
#define UHS_HOST_ERROR_TRANSFER_TIMEOUT 0xFFU
// SEI interaction defaults
#define UHS_HOST_TRANSFER_MAX_MS 10000 // USB transfer timeout in ms, per section 9.2.6.1 of USB 2.0 spec
#define UHS_HOST_TRANSFER_RETRY_MAXIMUM 3 // 3 retry limit for a transfer
#define UHS_HOST_DEBOUNCE_DELAY_MS 500 // settle delay in milliseconds
#define UHS_HUB_RESET_DELAY_MS 20 // hub port reset delay, 10ms recomended, but can be up to 20ms
//
// We only provide the minimum needed information for enumeration.
// Interface drivers should be able to set up what is needed with nothing more.
// A driver needs to know the following information:
// 1: address on the USB network, parent and port (aka UsbDeviceAddress)
// 2: endpoints
// 3: vid:pid, class, subclass, protocol
//
struct ENDPOINT_INFO {
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));
struct INTERFACE_INFO {
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t numep;
uint8_t klass;
uint8_t subklass;
uint8_t protocol;
ENDPOINT_INFO epInfo[16];
} __attribute__((packed));
struct ENUMERATION_INFO {
uint16_t vid;
uint16_t pid;
uint16_t bcdDevice;
uint8_t klass;
uint8_t subklass;
uint8_t protocol;
uint8_t bMaxPacketSize0;
uint8_t currentconfig;
uint8_t parent;
uint8_t port;
uint8_t address;
INTERFACE_INFO interface;
} __attribute__((packed));
/* USB Setup Packet Structure */
typedef struct {
// offset description
// 0 Bit-map of request type
union {
uint8_t bmRequestType;
struct {
uint8_t recipient : 5; // Recipient of the request
uint8_t type : 2; // Type of request
uint8_t direction : 1; // Direction of data transfer
} __attribute__((packed));
} ReqType_u;
// 1 Request
uint8_t bRequest;
// 2 Depends on bRequest
union {
uint16_t wValue;
struct {
uint8_t wValueLo;
uint8_t wValueHi;
} __attribute__((packed));
} wVal_u;
// 4 Depends on bRequest
uint16_t wIndex;
// 6 Depends on bRequest
uint16_t wLength;
// 8 bytes total
} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT;
// little endian :-) 8 8 8 8 16 16
#define mkSETUP_PKT8(bmReqType, bRequest, wValLo, wValHi, wInd, total) ((uint64_t)(((uint64_t)(bmReqType)))|(((uint64_t)(bRequest))<<8)|(((uint64_t)(wValLo))<<16)|(((uint64_t)(wValHi))<<24)|(((uint64_t)(wInd))<<32)|(((uint64_t)(total)<<48)))
#define mkSETUP_PKT16(bmReqType, bRequest, wVal, wInd, total) ((uint64_t)(((uint64_t)(bmReqType)))|(((uint64_t)(bRequest))<<8)|(((uint64_t)(wVal ))<<16) |(((uint64_t)(wInd))<<32)|(((uint64_t)(total)<<48)))
// Big endian -- but we aren't able to use this :-/
//#define mkSETUP_PKT8(bmReqType, bRequest, wValLo, wValHi, wInd, total) ((uint64_t)(((uint64_t)(bmReqType))<<56)|(((uint64_t)(bRequest))<<48)|(((uint64_t)(wValLo))<<40)|(((uint64_t)(wValHi))<<32)|(((uint64_t)(wInd))<<16)|((uint64_t)(total)))
//#define mkSETUP_PKT16(bmReqType, bRequest, wVal, wInd, total) ((uint64_t)(((uint64_t)(bmReqType))<<56)|(((uint64_t)(bRequest))<<48) |(((uint64_t)(wVal))<<32) |(((uint64_t)(wInd))<<16)|((uint64_t)(total)))
#endif /* USBCORE_H */

View File

@ -0,0 +1,248 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if !defined(_UHS_host_h_) || defined(__ADDRESS_H__)
#error "Never include UHS_address.h directly; include UHS_Usb.h instead"
#else
#define __ADDRESS_H__
/* 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 UHS_USB_NAK_MAX_POWER 14 // NAK binary order maximum value
#define UHS_USB_NAK_DEFAULT 13 // default 16K-1 NAKs before giving up
#define UHS_USB_NAK_NOWAIT 1 // Single NAK stops transfer
#define UHS_USB_NAK_NONAK 0 // Do not count NAKs, stop retrying after USB Timeout. Try not to use this.
#define bmUSB_DEV_ADDR_PORT 0x07
#define bmUSB_DEV_ADDR_PARENT 0x78
#define bmUSB_DEV_ADDR_HUB 0x40
// TODO: embed parent?
struct UHS_EpInfo {
uint8_t epAddr; // Endpoint address
uint8_t bIface;
uint16_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 bmNeedPing : 1; // 1 == ping protocol needed for next out packet
uint8_t bmNakPower : 5; // Binary order for NAK_LIMIT value
} __attribute__((packed));
};
} __attribute__((packed));
// TODO: embed parent address and port into epinfo struct,
// and nuke this address stupidity.
// This is a compact scheme. Should also support full spec.
// This produces a 7 hub limit, 49 devices + 7 hubs, 56 total.
//
// 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 number
// A - port number of parent
//
struct UHS_DeviceAddress {
union {
struct {
uint8_t bmAddress : 3; // 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));
struct UHS_Device {
volatile UHS_EpInfo *epinfo[UHS_HOST_MAX_INTERFACE_DRIVERS]; // endpoint info pointer
UHS_DeviceAddress address;
uint8_t epcount; // number of endpoints
uint8_t speed; // indicates device speed
} __attribute__((packed));
typedef void (*UsbDeviceHandleFunc)(UHS_Device *pdev);
class AddressPool {
UHS_EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device
// In order to avoid hub address duplication, this should use bits
uint8_t hubCounter; // hub counter
UHS_Device thePool[UHS_HOST_MAX_INTERFACE_DRIVERS];
// Initializes address pool entry
void UHS_NI InitEntry(uint8_t index) {
thePool[index].address.devAddress = 0;
thePool[index].epcount = 1;
thePool[index].speed = 0;
for(uint8_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) {
thePool[index].epinfo[i] = &dev0ep;
}
};
// Returns thePool index for a given address
uint8_t UHS_NI FindAddressIndex(uint8_t address = 0) {
for(uint8_t i = 1; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) {
if(thePool[i].address.devAddress == address)
return i;
}
return 0;
};
// Returns thePool child index for a given parent
uint8_t UHS_NI FindChildIndex(UHS_DeviceAddress addr, uint8_t start = 1) {
for(uint8_t i = (start < 1 || start >= UHS_HOST_MAX_INTERFACE_DRIVERS) ? 1 : start; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) {
if(thePool[i].address.bmParent == addr.bmAddress)
return i;
}
return 0;
};
// Frees address entry specified by index parameter
void UHS_NI FreeAddressByIndex(uint8_t index) {
// Zero field is reserved and should not be affected
if(index == 0)
return;
UHS_DeviceAddress 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);
// FIXME: use BIT MASKS
// If the hub had the last allocated address, hubCounter should be decremented
if(hubCounter == uda.bmAddress)
hubCounter--;
}
InitEntry(index);
}
void InitAllAddresses() {
for(uint8_t i = 1; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) InitEntry(i);
hubCounter = 0;
};
public:
AddressPool() {
hubCounter = 0;
// Zero address is reserved
InitEntry(0);
thePool[0].epinfo[0] = &dev0ep;
dev0ep.epAddr = 0;
#if UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
dev0ep.maxPktSize = 0x40; //starting at 0x40 and work down
#else
dev0ep.maxPktSize = 0x08;
#endif
dev0ep.epAttribs = 0; //set DATA0/1 toggles to 0
dev0ep.bmNakPower = UHS_USB_NAK_MAX_POWER;
InitAllAddresses();
};
// Returns a pointer to a specified address entry
UHS_Device* UHS_NI GetUsbDevicePtr(uint8_t addr) {
if(!addr)
return thePool;
uint8_t index = FindAddressIndex(addr);
return (!index) ? NULL : &thePool[index];
};
// Allocates new address
uint8_t UHS_NI AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 1) {
/* if (parent != 0 && port == 0)
USB_HOST_SERIAL.println("PRT:0"); */
UHS_DeviceAddress _parent;
_parent.devAddress = parent;
if(_parent.bmReserved || port > 7)
//if(parent > 127 || port > 7)
return 0;
// FIXME: use BIT MASKS
if(is_hub && hubCounter == 7)
return 0;
// finds first empty address entry starting from one
uint8_t index = FindAddressIndex(0);
if(!index) // if empty entry is not found
return 0;
UHS_DeviceAddress addr;
addr.devAddress = port;
addr.bmParent = _parent.bmAddress;
// FIXME: use BIT MASKS
if(is_hub) {
hubCounter++;
addr.bmHub = 1;
addr.bmAddress = hubCounter;
}
thePool[index].address = addr;
#if DEBUG_PRINTF_EXTRA_HUGE
#ifdef UHS_DEBUG_USB_ADDRESS
printf("Address: %x (%x.%x.%x)\r\n", addr.devAddress, addr.bmHub, addr.bmParent, addr.bmAddress);
#endif
#endif
return thePool[index].address.devAddress;
};
void UHS_NI FreeAddress(uint8_t addr) {
// if the root hub is disconnected all the addresses should be initialized
if(addr == 0x41) {
InitAllAddresses();
return;
}
uint8_t index = FindAddressIndex(addr);
FreeAddressByIndex(index);
};
};
#endif // __ADDRESS_H__

View File

@ -0,0 +1,70 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if !defined(_usb_h_) || defined(__HEXDUMP_H__)
#error "Never include UHS_hexdump.h directly; include UHS_Usb.h instead"
#else
#define __HEXDUMP_H__
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;
};
virtual 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) {
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;
}
}
}
}
#endif // __HEXDUMP_H__

View File

@ -0,0 +1,111 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 */
#ifndef _UHS_host_h_
#define _UHS_host_h_
// WARNING: Do not change the order of includes, or stuff will break!
#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#if DISABLED(USE_UHS3_USB)
#include <ISR_safe_memory.h>
#include <Wire.h>
#include <SPI.h>
#include <UHS_ByteBuffer.h>
#endif
#include "UHS_macros.h"
// None of these should ever be directly included by a driver, or a user's sketch.
#include "../dyn_SWI/dyn_SWI.h"
#include "UHS_USB_IDs.h"
#include "UHS_settings.h"
#include "UHS_usb_ch9.h"
#include "UHS_UsbCore.h"
#include "UHS_address.h"
#include "UHS_usbhost.h"
#include "UHS_printhex.h"
#include "UHS_message.h"
// Load system components as required
#if defined(LOAD_USB_HOST_SYSTEM) && !defined(USB_HOST_SYSTEM_LOADED)
#include "UHS_util_INLINE.h"
#include "UHS_host_INLINE.h"
#include "UHS_printf_HELPER.h"
#ifdef LOAD_USB_HOST_SHIELD
#include "USB_HOST_SHIELD/USB_HOST_SHIELD.h"
#endif
#if defined(LOAD_UHS_KINETIS_FS_HOST) && !defined(UHS_KINETIS_FS_HOST_LOADED)
#include "UHS_KINETIS_FS_HOST/UHS_KINETIS_FS_HOST.h"
#endif
#if defined(LOAD_UHS_KINETIS_EHCI) && !defined(UHS_KINETIS_EHCI_LOADED)
#include "UHS_KINETIS_EHCI/UHS_KINETIS_EHCI.h"
#endif
// Load USB drivers and multiplexers
#ifdef LOAD_UHS_HUB
#include "UHS_HUB/UHS_HUB.h"
#endif // HUB loaded
#ifdef LOAD_UHS_BULK_STORAGE
#include "UHS_BULK_STORAGE/UHS_BULK_STORAGE.h"
#endif
#ifdef LOAD_GENERIC_STORAGE
#include "../UHS_FS/UHS_FS.h"
#endif
// Add BT and optionally HID if directed to do so
#ifdef LOAD_UHS_BT
#include "UHS_BT/UHS_BT.h"
#endif // BT and optionally HID loaded
// Add HID
#ifdef LOAD_UHS_HID
#include "UHS_HID/UHS_HID.h"
#endif // HID loaded
// Add CDC multiplexers (currently only ACM)
#if defined(LOAD_UHS_CDC_ACM) || defined(LOAD_UHS_CDC_ACM_FTDI) || defined(LOAD_UHS_CDC_ACM_PROLIFIC) || defined(LOAD_UHS_CDC_ACM_XR21B1411)
#include "UHS_CDC/UHS_CDC.h"
#endif // CDC loaded
#ifdef LOAD_UHS_ADK
#include "UHS_ADK/UHS_ADK.h"
#endif
#ifdef LOAD_UHS_MIDI
#include "UHS_MIDI/UHS_MIDI.h"
#endif
#endif // System code loaded
#endif // _UHS_host_h_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,230 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 MACROS_H
#define MACROS_H
#include "macro_logic.h"
/*
* Universal Arduino(tm) "IDE" fixups.
*/
// Just in case...
#ifndef SERIAL_PORT_MONITOR
#define SERIAL_PORT_MONITOR Serial
#endif
#ifndef INT16_MIN
#define INT16_MIN -32768
#endif
// require 10607+
#if defined(ARDUINO) && ARDUINO >=10607
// nop :-)
#else
#error "Arduino version too old, and must be at least 1.6.7"
#endif
// Nuke screwed up macro junk from the IDE.
#ifdef __cplusplus
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif
#endif
#ifndef UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE
#ifndef UHS_BIG_FLASH
#if defined(FLASHEND) && defined(FLASHSTART)
#if (FLASHEND - FLASHSTART) > 0x0FFFFU
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif
#elif defined(__PIC32_FLASH_SIZE)
#if __PIC32_FLASH_SIZE > 511
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif
#elif defined(FLASHEND) && !defined(FLASHSTART)
// Assumes flash starts at 0x00000, is this a safe assumption?
// 192K + should be OK
#if FLASHEND > 0x02FFFFU
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif
#elif defined(IFLASH_SIZE)
#if IFLASH_SIZE > 0x0FFFFU
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif
#elif defined(ESP8266)
#define UHS_BIG_FLASH 1
#define SYSTEM_OR_SPECIAL_YIELD(...) yield()
#elif defined(__arm__) && defined(CORE_TEENSY)
#define UHS_BIG_FLASH 1
#elif defined(ARDUINO_spresense_ast)
#define UHS_BIG_FLASH 1
#else
// safe default
#warning Small flash?
#define UHS_BIG_FLASH 0
#endif
#endif
#if UHS_BIG_FLASH
#define UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE 1
#else
#define UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE 0
#endif
#endif
#if defined(__arm__) && defined(CORE_TEENSY)
#define UHS_PIN_WRITE(p, v) digitalWriteFast(p, v)
#define UHS_PIN_READ(p) digitalReadFast(p)
#endif
// TODO: Fast inline code for AVR and SAM based microcontrollers
// This can be done pretty easily.
// For now, this will just work out-of-the-box.
#ifndef UHS_PIN_WRITE
#define UHS_PIN_WRITE(p, v) digitalWrite(p, v)
#endif
#ifndef UHS_PIN_READ
#define UHS_PIN_READ(p) digitalRead(p)
#endif
#if defined( __PIC32MX__ ) && !defined(interrupts) // compiling with Microchip XC32 compiler
#define interrupts() __builtin_enable_interrupts()
#edfine noInterrupts() __builtin_disable_interrupts()
#endif
#ifndef ARDUINO_SAMD_ZERO
#ifdef ARDUINO_AVR_ADK
#define UHS_GET_DPI(x) (x == 54 ? 6 : digitalPinToInterrupt(x))
#else
#define UHS_GET_DPI(x) digitalPinToInterrupt(x)
#endif
#else
#define UHS_GET_DPI(x) (x)
#endif
#include "../../../../HAL/shared/progmem.h"
////////////////////////////////////////////////////////////////////////////////
// HANDY MACROS
////////////////////////////////////////////////////////////////////////////////
// Atmoically set/clear single bits using bitbands.
// Believe it or not, this boils down to a constant,
// and is less code than using |= &= operators.
// Bonus, it makes code easier to read too.
// Bitbanding is a wonderful thing.
#define BITNR(i) (i&0x1?0:i&0x2?1:i&0x4?2:i&0x8?3:i&0x10?4:i&0x20?5:i&0x40?6:i&0x80?7:i&0x100?8:i&0x200?9:i&0x400?10:i&0x800?11:i&0x1000?12:i&0x2000?13:i&0x4000?14:i&0x8000?15:i&0x10000?16:i&0x20000?17:i&0x40000?18:i&0x80000?19:i&0x100000?20:i&0x200000?21:i&0x400000?22:i&0x800000?23:i&0x1000000?24:i&0x2000000?25:i&0x4000000?26:i&0x8000000?27:i&0x10000000?28:i&0x20000000?29:i&0x40000000?30:i&0x80000000?31:32)
#define UHS_KIO_BITBAND_ADDR(r, i) (((uint32_t)&(r) - 0x40000000) * 32 + (i) * 4 + 0x42000000)
#define UHS_KIO_SETBIT_ATOMIC(r, m) (*(uint32_t *)UHS_KIO_BITBAND_ADDR((r), BITNR((m)))) = 1
#define UHS_KIO_CLRBIT_ATOMIC(r, m) (*(uint32_t *)UHS_KIO_BITBAND_ADDR((r), BITNR((m)))) = 0
#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 UHS_SWAP_VALUES(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 UHS_UINT8_BYTE0(__usi__) (((uint8_t *)&(__usi__))[0])
#define UHS_UINT8_BYTE1(__usi__) (((uint8_t *)&(__usi__))[1])
#define UHS_UINT8_BYTE2(__usi__) (((uint8_t *)&(__usi__))[2])
#define UHS_UINT8_BYTE3(__usi__) (((uint8_t *)&(__usi__))[3])
#define UHS_UINT8_BYTE4(__usi__) (((uint8_t *)&(__usi__))[4])
#define UHS_UINT8_BYTE5(__usi__) (((uint8_t *)&(__usi__))[5])
#define UHS_UINT8_BYTE6(__usi__) (((uint8_t *)&(__usi__))[6])
#define UHS_UINT8_BYTE7(__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 UHS_UINT8_BYTE0(__usi__) ((uint8_t)((__usi__) & 0xFF ))
#define UHS_UINT8_BYTE1(__usi__) ((uint8_t)(((__usi__) >> 8) & 0xFF))
#define UHS_UINT8_BYTE2(__usi__) ((uint8_t)(((__usi__) >> 16) & 0xFF))
#define UHS_UINT8_BYTE3(__usi__) ((uint8_t)(((__usi__) >> 24) & 0xFF))
#define UHS_UINT8_BYTE4(__usi__) ((uint8_t)(((__usi__) >> 32) & 0xFF))
#define UHS_UINT8_BYTE5(__usi__) ((uint8_t)(((__usi__) >> 40) & 0xFF))
#define UHS_UINT8_BYTE6(__usi__) ((uint8_t)(((__usi__) >> 48) & 0xFF))
#define UHS_UINT8_BYTE7(__usi__) ((uint8_t)(((__usi__) >> 56) & 0xFF))
#endif
#define UHS_UINT16_SET_BYTE1(__usi__) ((uint16_t)(__usi__) << 8)
#define UHS_UINT32_SET_BYTE1(__usi__) ((uint32_t)(__usi__) << 8)
#define UHS_UINT64_SET_BYTE1(__usi__) ((uint64_t)(__usi__) << 8)
#define UHS_UINT32_SET_BYTE2(__usi__) ((uint32_t)(__usi__) << 16)
#define UHS_UINT64_SET_BYTE2(__usi__) ((uint64_t)(__usi__) << 16)
#define UHS_UINT32_SET_BYTE3(__usi__) ((uint32_t)(__usi__) << 24)
#define UHS_UINT64_SET_BYTE3(__usi__) ((uint64_t)(__usi__) << 24)
#define UHS_UINT64_SET_BYTE4(__usi__) ((uint64_t)(__usi__) << 32)
#define UHS_UINT64_SET_BYTE5(__usi__) ((uint64_t)(__usi__) << 40)
#define UHS_UINT64_SET_BYTE6(__usi__) ((uint64_t)(__usi__) << 48)
#define UHS_UINT64_SET_BYTE7(__usi__) ((uint64_t)(__usi__) << 56)
// These are the smallest and fastest ways I have found so far in pure C/C++.
#define UHS_BYTES_TO_UINT16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)UHS_UINT16_SET_BYTE1(__usc1__)))
#define UHS_BYTES_TO_UINT32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | UHS_UINT32_SET_BYTE1(__usc1__) | UHS_UINT32_SET_BYTE2(__usc2__) | UHS_UINT32_SET_BYTE3(__usc3__)))
#define UHS_BYTES_TO_UINT64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | UHS_UINT64_SET_BYTE1(__usc1__) | UHS_UINT64_SET_BYTE2(__usc2__) | UHS_UINT64_SET_BYTE3(__usc3__) | UHS_UINT64_SET_BYTE4(__usc4__) | UHS_UINT64_SET_BYTE5(__usc5__) | UHS_UINT64_SET_BYTE6(__usc6__) | UHS_UINT64_SET_BYTE7(__usc7__)))
#endif
/*
* Debug macros.
* Useful when porting from UHS2.
* Do not use these for any new code.
* Change to better debugging after port is completed.
* Strings are stored in progmem (flash) instead of RAM.
*/
#define USBTRACE1(s,l) (Notify(PSTR(s), l))
#define USBTRACE(s) (USBTRACE1((s), 0x80)); USB_HOST_SERIAL.flush()
#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR("\r\n"), l))
#define USBTRACE3X(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l))
#define USBTRACE2(s,r) (USBTRACE3((s),(r),0x80)); USB_HOST_SERIAL.flush()
#define USBTRACE2X(s,r) (USBTRACE3X((s),(r),0x80)); USB_HOST_SERIAL.flush()
#define VOID0 ((void)0)
#ifndef NOTUSED
#define NOTUSED(...) __VA_ARGS__ __attribute__((unused))
#endif
#endif /* MACROS_H */

View File

@ -0,0 +1,91 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if !defined(_UHS_host_h_) || defined(__MESSAGE_H__)
#error "Never include UHS_message.h directly; include UHS_Usb.h instead"
#else
#define __MESSAGE_H__
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(...) VOID0
#define NotifyStr(...) VOID0
#define Notifyc(...) VOID0
#define NotifyFailGetDevDescr(...) VOID0
#define NotifyFailSetDevTblEntry(...) VOID0
#define NotifyFailGetConfDescr(...) VOID0
#define NotifyFailGetDevDescr(...) VOID0
#define NotifyFailSetDevTblEntry(...) VOID0
#define NotifyFailGetConfDescr(...) VOID0
#define NotifyFailSetConfDescr(...) VOID0
#define NotifyFailUnknownDevice(...) VOID0
#define NotifyFail(...) VOID0
#endif
#ifdef DEBUG_USB_HOST
template <class ERROR_TYPE> void ErrorMessage(uint8_t level, char const * msg, ERROR_TYPE rcode = 0) {
Notify(msg, level);
Notify(PSTR(": "), level);
D_PrintHex<ERROR_TYPE > (rcode, level);
Notify(PSTR("\r\n"), level);
#else
template <class ERROR_TYPE> void ErrorMessage(NOTUSED(uint8_t level), NOTUSED(char const * msg), ERROR_TYPE rcode = 0) {
(void)rcode;
#endif
}
#ifdef DEBUG_USB_HOST
template <class ERROR_TYPE> void ErrorMessage(char const * msg, ERROR_TYPE rcode = 0) {
Notify(msg, 0x80);
Notify(PSTR(": "), 0x80);
D_PrintHex<ERROR_TYPE > (rcode, 0x80);
Notify(PSTR("\r\n"), 0x80);
#else
template <class ERROR_TYPE> void ErrorMessage(NOTUSED(char const * msg), ERROR_TYPE rcode = 0) {
(void)rcode;
#endif
}
#endif // __MESSAGE_H__

View File

@ -0,0 +1,200 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 UHS_PRINTF_HELPER_H
#define UHS_PRINTF_HELPER_H
#ifdef LOAD_UHS_PRINTF_HELPER
#include <Arduino.h>
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif
#ifndef STDIO_IS_OK_TO_USE_AS_IS
#if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAM_DUE) || defined(ARDUINO_spresense_ast)
// STDIO patching not required.
#define STDIO_IS_OK_TO_USE_AS_IS
#endif
#endif
#ifndef STDIO_IS_OK_TO_USE_AS_IS
// We need to patch STDIO so it can be used.
#ifndef SERIAL_PORT_MONITOR
// Some don't define this.
#define SERIAL_PORT_MONITOR Serial
#endif
#ifndef SERIAL_PORT_HARDWARE
// Some don't define this.
#define SERIAL_PORT_HARDWARE SERIAL_PORT_MONITOR
#endif
#ifndef USB_HOST_SERIAL
#if defined(SERIAL_PORT_USBVIRTUAL) && defined(LOAD_UHS_KINETIS_FS_HOST)
#define USB_HOST_SERIAL SERIAL_PORT_HARDWARE
#else
#define USB_HOST_SERIAL SERIAL_PORT_MONITOR
#endif
#endif
#ifndef NOTUSED
#define NOTUSED(...) __VA_ARGS__ __attribute__((unused))
#endif
#ifndef __AVR__
#ifndef printf_P
#define printf_P(...) printf(__VA_ARGS__)
#endif
#endif
#ifdef ARDUINO_ARCH_PIC32
/*
* For printf() output with pic32 Arduino
*/
extern "C" {
void _mon_putc(char s) {
USB_HOST_SERIAL.write(s);
}
int _mon_getc() {
while(!USB_HOST_SERIAL.available());
return USB_HOST_SERIAL.read();
}
}
#elif defined(__AVR__)
extern "C" {
static FILE tty_stdio;
static FILE tty_stderr;
static int NOTUSED(tty_stderr_putc(char c, NOTUSED(FILE *t)));
static int NOTUSED(tty_stderr_flush(NOTUSED(FILE *t)));
static int NOTUSED(tty_std_putc(char c, NOTUSED(FILE *t)));
static int NOTUSED(tty_std_getc(NOTUSED(FILE *t)));
static int NOTUSED(tty_std_flush(NOTUSED(FILE *t)));
static int tty_stderr_putc(char c, NOTUSED(FILE *t)) {
USB_HOST_SERIAL.write(c);
return 0;
}
static int tty_stderr_flush(NOTUSED(FILE *t)) {
USB_HOST_SERIAL.flush();
return 0;
}
static int tty_std_putc(char c, NOTUSED(FILE *t)) {
USB_HOST_SERIAL.write(c);
return 0;
}
static int tty_std_getc(NOTUSED(FILE *t)) {
while(!USB_HOST_SERIAL.available());
return USB_HOST_SERIAL.read();
}
static int tty_std_flush(NOTUSED(FILE *t)) {
USB_HOST_SERIAL.flush();
return 0;
}
}
#elif defined(CORE_TEENSY)
extern "C" {
int _write(int fd, const char *ptr, int len) {
int j;
for(j = 0; j < len; j++) {
if(fd == 1)
USB_HOST_SERIAL.write(*ptr++);
else if(fd == 2)
USB_HOST_SERIAL.write(*ptr++);
}
return len;
}
int _read(int fd, char *ptr, int len) {
if(len > 0 && fd == 0) {
while(!USB_HOST_SERIAL.available());
*ptr = USB_HOST_SERIAL.read();
return 1;
}
return 0;
}
#include <sys/stat.h>
int _fstat(int fd, struct stat *st) {
memset(st, 0, sizeof (*st));
st->st_mode = S_IFCHR;
st->st_blksize = 1024;
return 0;
}
int _isatty(int fd) {
return (fd < 3) ? 1 : 0;
}
}
#else
#error no STDIO
#endif // defined(ARDUINO_ARCH_PIC32)
#ifdef __AVR__
// The only wierdo in the bunch...
void UHS_AVR_printf_HELPER_init() {
// Set up stdio/stderr
tty_stdio.put = tty_std_putc;
tty_stdio.get = tty_std_getc;
tty_stdio.flags = _FDEV_SETUP_RW;
tty_stdio.udata = 0;
tty_stderr.put = tty_stderr_putc;
tty_stderr.get = NULL;
tty_stderr.flags = _FDEV_SETUP_WRITE;
tty_stderr.udata = 0;
stdout = &tty_stdio;
stdin = &tty_stdio;
stderr = &tty_stderr;
}
#define UHS_printf_HELPER_init() UHS_AVR_printf_HELPER_init()
#endif
#endif /* STDIO_IS_OK_TO_USE_AS_IS */
#endif /* load.... */
#ifndef UHS_printf_HELPER_init
#define UHS_printf_HELPER_init() (void(0))
#endif
#endif /* UHS_PRINTF_HELPER_H */

View File

@ -0,0 +1,96 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if !defined(_UHS_host_h_) || defined(__PRINTHEX_H__)
#error "Never include UHS_printhex.h directly; include UHS_Usb.h instead"
#else
#define __PRINTHEX_H__
void E_Notifyc(char c, int lvl);
template <class T>
void PrintHex(T val, int lvl) {
int num_nybbles = sizeof (T) * 2;
do {
char v = 48 + (((val >> (num_nybbles - 1) * 4)) & 0x0F);
if(v > 57) v += 7;
E_Notifyc(v, lvl);
} while(--num_nybbles);
}
template <class T>
void PrintBin(T val, int lvl) {
for(T mask = (((T)1) << ((sizeof (T) << 3) - 1)); mask; mask >>= 1)
if(val & mask)
E_Notifyc('1', lvl);
else
E_Notifyc('0', lvl);
}
template <class T>
void SerialPrintHex(T val) {
int num_nybbles = sizeof (T) * 2;
do {
char v = 48 + (((val >> (num_nybbles - 1) * 4)) & 0x0F);
if(v > 57) v += 7;
USB_HOST_SERIAL.print(v);
} while(--num_nybbles);
}
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);
}
#ifdef DEBUG_USB_HOST
template <class T> void D_PrintHex(T val, int lvl) {
PrintHex<T > (val, lvl);
#else
template <class T> void D_PrintHex(NOTUSED(T val), NOTUSED(int lvl)) {
#endif
}
#ifdef DEBUG_USB_HOST
template <class T> void D_PrintBin(T val, int lvl) {
PrintBin<T > (val, lvl);
#else
template <class T> void D_PrintBin(NOTUSED(T val), NOTUSED(int lvl)) {
#endif
}
#endif // __PRINTHEX_H__

View File

@ -0,0 +1,141 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 UHS_SETTINGS_H
#define UHS_SETTINGS_H
// TO-DO: Move specific settings to modules which use them.
////////////////////////////////////////////////////////////////////////////////
// Define any of these options at the top of your sketch to override
// the defaults contained herewith. Do NOT do modifications here.
// Individual Components have their own settings.
//
// Macro | Settings and notes | Default
// -----------------------------+-----------------------+-----------------------
// | Any class that does |
// USB_HOST_SERIAL | text streaming | SERIAL_PORT_MONITOR
// | e.g. Serial2 |
// -----------------------------+-----------------------+-----------------------
// ENABLE_UHS_DEBUGGING | 0 = off, 1 = on | 0
// -----------------------------+-----------------------+-----------------------
// | 0 = off, 1 = on |
// | Caution! Can make |
// DEBUG_PRINTF_EXTRA_HUGE | program too large! | 0
// | Other modules depend |
// | on this setting. |
// -----------------------------+-----------------------+-----------------------
// USE_UHS_BLACK_WIDDOW | 0 = no, 1 = yes | 0
// -----------------------------+-----------------------+-----------------------
// ENABLE_WII_IR_CAMERA | 0 = no, 1 = yes | 0
// -----------------------------^-----------------------^-----------------------
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// DEBUGGING
////////////////////////////////////////////////////////////////////////////////
#ifndef USB_HOST_SERIAL
#if defined(SERIAL_PORT_USBVIRTUAL) && defined(LOAD_UHS_KINETIS_FS_HOST)
#define USB_HOST_SERIAL SERIAL_PORT_HARDWARE
#else
#define USB_HOST_SERIAL SERIAL_PORT_MONITOR
#endif
#endif
#ifndef ENABLE_UHS_DEBUGGING
#define ENABLE_UHS_DEBUGGING 0
#endif
#ifndef DEBUG_PRINTF_EXTRA_HUGE
#define DEBUG_PRINTF_EXTRA_HUGE 0
#endif
////////////////////////////////////////////////////////////////////////////////
// Manual board activation
////////////////////////////////////////////////////////////////////////////////
/* Set this to 1 if you are using a Black Widdow */
#ifndef USE_UHS_BLACK_WIDDOW
#define USE_UHS_BLACK_WIDDOW 0
#endif
////////////////////////////////////////////////////////////////////////////////
// Wii IR camera
////////////////////////////////////////////////////////////////////////////////
/* Set this to 1 to activate code for the Wii IR camera */
#ifndef ENABLE_WII_IR_CAMERA
#define ENABLE_WII_IR_CAMERA 0
#endif
////////////////////////////////////////////////////////////////////////////////
// Set to 1 to use the faster spi4teensy3 driver. (not used yet))
////////////////////////////////////////////////////////////////////////////////
#ifndef 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!
#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
#define UHS_SLEEP_MS(v) pUsb->sof_delay(v)
#ifndef UHS_NI
#define UHS_NI __attribute__((noinline))
#endif
#endif /* SETTINGS_H */

View File

@ -0,0 +1,222 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if !defined(_UHS_host_h_) || defined(_UHS_ch9_h_)
#error "Never include UHS_usb_ch9.h directly; include UHS_Usb.h instead"
#else
/* USB chapter 9 structures */
#define _UHS_ch9_h_
/* 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
/* Wireless USB Device Requests */
#define USB_REQ_SET_ENCRYPTION 0x0D
#define USB_REQ_GET_ENCRYPTION 0x0E
#define USB_REQ_RPIPE_ABORT 0x0E
#define USB_REQ_SET_HANDSHAKE 0x0F
#define USB_REQ_RPIPE_RESET 0x0F
#define USB_REQ_GET_HANDSHAKE 0x10
#define USB_REQ_SET_CONNECTION 0x11
#define USB_REQ_SET_SECURITY_DATA 0x12
#define USB_REQ_GET_SECURITY_DATA 0x13
#define USB_REQ_SET_WUSB_DATA 0x14
#define USB_REQ_LOOPBACK_DATA_WRITE 0x15
#define USB_REQ_LOOPBACK_DATA_READ 0x16
#define USB_REQ_SET_INTERFACE_DS 0x17
/* USB feature flags */
#define USB_DEVICE_SELF_POWERED 0 /* (read only) */
#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */
#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */
#define USB_DEVICE_BATTERY 2 /* (wireless) */
#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */
#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/
#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */
#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */
#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */
#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
/* 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
/* 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
#define USB_SETUP_RECIPIENT_PORT 0x04 // Wireless USB 1.0
#define USB_SETUP_RECIPIENT_RPIPE 0x05 // Wireless USB 1.0
/* 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 USB_DESCRIPTOR_DEBUG 0x0A
#define USB_DESCRIPTOR_INTERFACE_ASSOCIATION 0x0B
#define USB_DESCRIPTOR_SECURITY 0x0C
#define USB_DESCRIPTOR_KEY 0x0D
#define USB_DESCRIPTOR_ENCRYPTION_TYPE 0x0E
#define USB_DESCRIPTOR_BOS 0x0F
#define USB_DESCRIPTOR_DEVICE_CAPABILITY 0x10
#define USB_DESCRIPTOR_WIRELESS_ENDPOINT_COMP 0x11
#define USB_DESCRIPTOR_WIRE_ADAPTER 0x21
#define USB_DESCRIPTOR_RPIPE 0x22
#define USB_DESCRIPTOR_CS_RADIO_CONTROL 0x23
#define USB_DESCRIPTOR_SS_ENDPOINT_COMP 0x30
#define USB_HID_DESCRIPTOR 0x21
// Conventional codes for class-specific descriptors. "Common Class" Spec (3.11)
#define USB_DESCRIPTOR_CS_DEVICE 0x21
#define USB_DESCRIPTOR_CS_CONFIG 0x22
#define USB_DESCRIPTOR_CS_STRING 0x23
#define USB_DESCRIPTOR_CS_INTERFACE 0x24
#define USB_DESCRIPTOR_CS_ENDPOINT 0x25
/* 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
#define USB_TRANSFER_DIRECTION_IN 0x80 // Indicate direction is IN
/* 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;
#endif // _ch9_h_

View File

@ -0,0 +1,449 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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 _UHS_host_h_
#error "Never include UHS_usbhost.h directly; include UHS_host.h instead"
#else
#ifndef _USBHOST_H_
#define _USBHOST_H_
// Very early prototypes
#ifdef UHS_LOAD_BT
void UHS_BT_SetUSBInterface(UHS_USB_HOST_BASE *host, ENUMERATION_INFO *ei);
void UHS_BT_ScanUninitialized(UHS_USB_HOST_BASE *host);
void UHS_BT_Poll(UHS_USB_HOST_BASE *host);
#endif
#ifdef UHS_LOAD_HID
void UHS_HID_SetUSBInterface(UHS_USB_HOST_BASE *host, ENUMERATION_INFO *ei);
void UHS_HID_ScanUninitialized(UHS_USB_HOST_BASE *host);
void UHS_HID_Poll(UHS_USB_HOST_BASE *host);
#endif
//#if defined(LOAD_UHS_CDC_ACM) || defined(LOAD_UHS_CDC_ACM_FTDI) || defined(LOAD_UHS_CDC_ACM_PROLIFIC) || defined(LOAD_UHS_CDC_ACM_XR21B1411)
//void UHS_CDC_ACM_SetUSBInterface(UHS_USB_HOST_BASE *host, ENUMERATION_INFO *ei);
//void UHS_CDC_ACM_ScanUninitialized(UHS_USB_HOST_BASE *host);
//void UHS_CDC_ACM_Poll(UHS_USB_HOST_BASE *host);
//#endif
class UHS_USBInterface; // forward class declaration
// enumerator to turn the VBUS on/off
typedef enum {
vbus_on = 0,
vbus_off = 1
} VBUS_t;
// All host SEI use this base class
class UHS_USB_HOST_BASE {
public:
AddressPool addrPool;
UHS_USBInterface* devConfig[UHS_HOST_MAX_INTERFACE_DRIVERS];
volatile uint8_t usb_error;
volatile uint8_t usb_task_state;
volatile uint8_t usb_task_polling_disabled;
volatile uint8_t usb_host_speed;
volatile uint8_t hub_present;
UHS_USB_HOST_BASE() {
for(uint16_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) {
devConfig[i] = NULL;
}
usb_task_polling_disabled = 0;
usb_task_state = UHS_USB_HOST_STATE_INITIALIZE; //set up state machine
usb_host_speed = 0;
usb_error = 0;
};
/////////////////////////////////////////////
//
// Virtual methods that interface to the SIE
// Overriding each is mandatory.
//
/////////////////////////////////////////////
/**
* Delay for x milliseconds
* Override if your controller provides an SOF IRQ, which may involve
* some sort of reentrant ISR or workaround with interrupts enabled.
*
* @param x how many milliseconds to delay
* @return true if delay completed without a state change, false if delay aborted
*/
virtual bool UHS_NI sof_delay(uint16_t x) {
if(!(usb_task_state & UHS_USB_HOST_STATE_MASK)) return false;
uint8_t current_state = usb_task_state;
while(current_state == usb_task_state && x--) {
delay(1);
}
return (current_state == usb_task_state);
};
virtual UHS_EpInfo * UHS_NI ctrlReqOpen(NOTUSED(uint8_t addr), NOTUSED(uint64_t Request), NOTUSED(uint8_t *dataptr)) {
return NULL;
};
virtual void UHS_NI vbusPower(NOTUSED(VBUS_t state)) {
};
virtual void UHS_NI Task() {
};
virtual uint8_t UHS_NI SetAddress(NOTUSED(uint8_t addr), NOTUSED(uint8_t ep), NOTUSED(UHS_EpInfo **ppep), NOTUSED(uint16_t &nak_limit)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t UHS_NI OutTransfer(NOTUSED(UHS_EpInfo *pep), NOTUSED(uint16_t nak_limit), NOTUSED(uint16_t nbytes), NOTUSED(uint8_t *data)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t UHS_NI InTransfer(NOTUSED(UHS_EpInfo *pep), NOTUSED(uint16_t nak_limit), NOTUSED(uint16_t *nbytesptr), NOTUSED(uint8_t *data)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t UHS_NI ctrlReqClose(NOTUSED(UHS_EpInfo *pep), NOTUSED(uint8_t bmReqType), NOTUSED(uint16_t left), NOTUSED(uint16_t nbytes), NOTUSED(uint8_t *dataptr)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t UHS_NI ctrlReqRead(NOTUSED(UHS_EpInfo *pep), NOTUSED(uint16_t *left), NOTUSED(uint16_t *read), NOTUSED(uint16_t nbytes), NOTUSED(uint8_t *dataptr)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t UHS_NI dispatchPkt(NOTUSED(uint8_t token), NOTUSED(uint8_t ep), NOTUSED(uint16_t nak_limit)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t UHS_NI init() {
return 0;
};
virtual void UHS_NI doHostReset() {
};
virtual int16_t UHS_NI Init(NOTUSED(int16_t mseconds)) {
return -1;
};
virtual int16_t UHS_NI Init() {
return Init(INT16_MIN);
};
virtual uint8_t hwlPowerUp() {
/* This is for machine specific support to enable/power up the USB HW to operate*/
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual uint8_t hwPowerDown() {
/* This is for machine specific support to disable/powerdown the USB Hw */
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
virtual bool IsHub(uint8_t klass) {
return (klass == UHS_USB_CLASS_HUB);
};
virtual void UHS_NI suspend_host() {
// Used on MCU that lack control of IRQ priority (AVR).
// Suspends ISRs, for critical code. IRQ will be serviced after it is resumed.
// NOTE: you must track the state yourself!
};
virtual void UHS_NI resume_host() {
// Used on MCU that lack control of IRQ priority (AVR).
// Resumes ISRs.
// NOTE: you must track the state yourself!
};
/////////////////////////////////////////////
//
// Built-ins, No need to override
//
/////////////////////////////////////////////
// these two probably will go away, and won't be used, TBD
inline void Poll_Others() {
#ifdef UHS_LOAD_BT
UHS_BT_Poll(this);
#endif
#ifdef UHS_LOAD_HID
UHS_HID_Poll(this);
#endif
}
inline void DisablePoll() {
noInterrupts();
usb_task_polling_disabled++;
DDSB();
interrupts();
}
inline void EnablePoll() {
noInterrupts();
usb_task_polling_disabled--;
DDSB();
interrupts();
}
uint8_t UHS_NI seekInterface(ENUMERATION_INFO *ei, uint16_t inf, USB_FD_CONFIGURATION_DESCRIPTOR *ucd);
uint8_t UHS_NI setEpInfoEntry(uint8_t addr, uint8_t iface, uint8_t epcount, volatile UHS_EpInfo* eprecord_ptr);
uint8_t UHS_NI EPClearHalt(uint8_t addr, uint8_t ep);
uint8_t UHS_NI ctrlReq(uint8_t addr, uint64_t Request, uint16_t nbytes, uint8_t *dataptr);
uint8_t UHS_NI getDevDescr(uint8_t addr, uint16_t nbytes, uint8_t *dataptr);
uint8_t UHS_NI getConfDescr(uint8_t addr, uint16_t nbytes, uint8_t conf, uint8_t *dataptr);
uint8_t UHS_NI setAddr(uint8_t oldaddr, uint8_t newaddr);
uint8_t UHS_NI setConf(uint8_t addr, uint8_t conf_value);
uint8_t UHS_NI getStrDescr(uint8_t addr, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t *dataptr);
void UHS_NI ReleaseDevice(uint8_t addr);
uint8_t UHS_NI Configuring(uint8_t parent, uint8_t port, uint8_t speed);
void UHS_NI DeviceDefaults(uint8_t maxep, UHS_USBInterface *device);
UHS_EpInfo* UHS_NI getEpInfoEntry(uint8_t addr, uint8_t ep);
inline uint8_t getUsbTaskState() {
return ( usb_task_state);
};
inline AddressPool* GetAddressPool() {
return &addrPool;
};
int UHS_NI RegisterDeviceClass(UHS_USBInterface *pdev) {
for(uint8_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++) {
if(!devConfig[i]) {
devConfig[i] = pdev;
return i;
}
}
//return UHS_HOST_ERROR_CANT_REGISTER_DEVICE_CLASS;
return -1;
};
#if 0
inline void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
addrPool.ForEachUsbDevice(pfunc);
};
#endif
uint8_t TestInterface(ENUMERATION_INFO *ei);
uint8_t enumerateInterface(ENUMERATION_INFO *ei);
uint8_t getNextInterface(ENUMERATION_INFO *ei, UHS_EpInfo *pep, uint8_t data[], uint16_t *left, uint16_t *read, uint8_t *offset);
uint8_t initDescrStream(ENUMERATION_INFO *ei, USB_FD_CONFIGURATION_DESCRIPTOR *ucd, UHS_EpInfo *pep, uint8_t *data, uint16_t *left, uint16_t *read, uint8_t *offset);
uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *data);
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data);
uint8_t doSoftReset(uint8_t parent, uint8_t port, uint8_t address);
uint8_t getone(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint8_t *dataptr, uint8_t *offset);
uint8_t eat(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint8_t *dataptr, uint8_t *offset, uint16_t *yum);
};
// All device interface drivers use this subclass
class UHS_USBInterface {
public:
UHS_USB_HOST_BASE *pUsb; // Parent USB host
volatile uint8_t bNumEP; // total number of EP in this interface
volatile UHS_EpInfo epInfo[16]; // This is a stub, override in the driver.
volatile uint8_t bAddress; // address of the device
volatile uint8_t bConfNum; // configuration number
volatile uint8_t bIface; // interface value
volatile bool bPollEnable; // poll enable flag, operating status
volatile uint32_t qNextPollTime; // next poll time
/**
* Resets interface driver to unused state. You should override this in
* your driver if it requires extra class variable cleanup.
*/
virtual void DriverDefaults() {
printf("Default driver defaults.\r\n");
pUsb->DeviceDefaults(bNumEP, this);
};
/**
* Checks if this interface is supported.
* Executed called when new devices are connected.
*
* @param ei
* @return true if the interface is supported
*/
virtual bool OKtoEnumerate(NOTUSED(ENUMERATION_INFO *ei)) {
return false;
};
/**
* Configures any needed endpoint information for an interface.
* You must provide this in your driver.
* Executed when new devices are connected and OKtoEnumerate()
* returned true.
*
* @param ei
* @return zero on success
*/
virtual uint8_t SetInterface(NOTUSED(ENUMERATION_INFO *ei)) {
return UHS_HOST_ERROR_NOT_IMPLEMENTED;
};
/**
* Interface specific additional setup and enumeration that
* can't occur when the descriptor stream is open.
* Also used for collection of unclaimed interfaces, to link to the master.
*
* @return zero on success
*/
virtual uint8_t Finalize() {
return 0;
};
/**
* Executed after interface is finalized but, before polling has started.
*
* @return 0 on success
*/
virtual uint8_t OnStart() {
return 0;
};
/**
* Start interface polling
* @return
*/
virtual uint8_t Start() {
uint8_t rcode = OnStart();
if(!rcode) bPollEnable = true;
return rcode;
};
/**
* Executed before anything else in Release().
*/
virtual void OnRelease() {
return;
};
/**
* Release resources when device is disconnected.
* Normally this does not need to be overridden.
*/
virtual void Release() {
OnRelease();
DriverDefaults();
return;
};
/**
* Executed After driver polls.
* Can be used when there is an important change detected during polling
* and you want to handle it elsewhere.
* Examples:
* Media status change for bulk, e.g. ready, not-ready, media changed, door opened.
* Button state/joystick position/etc changes on a HID device.
* Flow control status change on a communication device, e.g. CTS on serial
*/
virtual void OnPoll() {
return;
};
/**
* Poll interface driver. You should override this in your driver if you
* require polling faster or slower than every 100 milliseconds, or your
* driver requires special housekeeping.
*/
virtual void Poll() {
OnPoll();
qNextPollTime = millis() + 100;
};
virtual bool UHS_NI Polling() {
return bPollEnable;
}
/**
* This is only for a hub.
* @param port
*/
virtual void ResetHubPort(NOTUSED(uint8_t port)) {
return;
};
#if 0
/**
* @return true if this interface is Vendor Specific.
*/
virtual bool IsVSI() {
return false;
}
#endif
};
#if 0
/**
* Vendor Specific interface class.
* This is used by a partner interface.
* It can also be used to force-enumerate an interface that
* can use this interface directly.
* You can also add an instance of this class within the interface constructor
* if you expect the interface.
*
* If this is not needed, it may be removed. Nothing I have written needs this.
* Let me know if it is not required, then IsVSI method can also be shit-canned.
* -- AJK
*/
class UHS_VSI : public UHS_USBInterface {
public:
volatile UHS_EpInfo epInfo[1];
volatile ENUMERATION_INFO eInfo;
UHS_VSI(UHS_USB_HOST_BASE *p);
bool OKtoEnumerate(ENUMERATION_INFO *ei);
uint8_t SetInterface(ENUMERATION_INFO *ei);
virtual void DriverDefaults();
virtual void Release();
uint8_t GetAddress() {
return bAddress;
};
virtual bool IsVSI() {
return true;
}
};
#endif
#endif //_USBHOST_H_
#endif

View File

@ -0,0 +1,129 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
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
*/
#if defined(LOAD_USB_HOST_SYSTEM) && !defined(USB_HOST_SYSTEM_UTIL_LOADED)
#define USB_HOST_SYSTEM_UTIL_LOADED
// 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;
#if defined(ARDUINO) && ARDUINO >=100
USB_HOST_SERIAL.print(c);
#else
USB_HOST_SERIAL.print(c, BYTE);
#endif
//USB_HOST_SERIAL.flush();
}
void E_Notify(char const * msg, int lvl) {
if(UsbDEBUGlvl < lvl) return;
if(!msg) return;
char c;
while((c = pgm_read_byte(msg++))) E_Notifyc(c, lvl);
}
void E_NotifyStr(char const * msg, int lvl) {
if(UsbDEBUGlvl < lvl) return;
if(!msg) return;
char c;
while((c = *msg++)) E_Notifyc(c, lvl);
}
void E_Notify(uint8_t b, int lvl) {
if(UsbDEBUGlvl < lvl) return;
#if defined(ARDUINO) && ARDUINO >=100
USB_HOST_SERIAL.print(b);
#else
USB_HOST_SERIAL.print(b, DEC);
#endif
}
void E_Notify(double d, int lvl) {
if(UsbDEBUGlvl < lvl) return;
USB_HOST_SERIAL.print(d);
}
#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
#else
#error "Never include UHS_util_INLINE.h, include UHS_host.h instead"
#endif

View File

@ -0,0 +1,226 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : https://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(USB_HOST_SHIELD_H) || defined(_max3421e_h_)
#error "Never include UHS_max3421e.h directly; include USB_HOST_SHIELD.h instead"
#else
#define _max3421e_h_
/* MAX3421E register/bit names and bitmasks */
#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 // Receive FIFO Register
#define rSNDFIFO 0x10 // Send FIFO Register
#define rSUDFIFO 0x20 // Set Up Data FIFO Register
#define rRCVBC 0x30 // Receive FIFO Byte Count Register
#define rSNDBC 0x38 // Send FIFO Byte Count Register
// USB Interrupt Request Status (USBIRQ)
#define rUSBIRQ 0x68 // USB Interrupt Request Register
#define bmVBUSIRQ 0x40 // Vbus Present Interrupt Request
#define bmNOVBUSIRQ 0x20 // Vbus Absent Interrupt Request
#define bmOSCOKIRQ 0x01 // Oscillator OK Interrupt Request
// USB Interrupt Request Control (USBIEN)
#define rUSBIEN 0x70 // USB Interrupt Request Enable Register
#define bmVBUSIE bmVBUSIRQ // Vbus Present Interrupt Request Enable
#define bmNOVBUSIE bmNOVBUSIRQ // Vbus Absent Interrupt Request Enable
#define bmOSCOKIE bmOSCOKIRQ // Oscillator OK Interrupt Request Enable
// (USBCTL)
#define rUSBCTL 0x78 //15<<3
#define bmCHIPRES 0x20 //b5
#define bmPWRDOWN 0x10 //b4
// (CPUCTL)
#define rCPUCTL 0x80 //16<<3
#define bmPUSLEWID1 0x80 //b7
#define bmPULSEWID0 0x40 //b6
#define bmIE 0x01 //b0
// bmPUSLEWID1 bmPULSEWID0 Pulse width
// 0 0 10.6uS
// 0 1 5.3uS
// 1 0 2.6uS
// 1 1 1.3uS
#define PUSLEWIDTH10_6 (0)
#define PUSLEWIDTH5_3 (bmPULSEWID0)
#define PUSLEWIDTH2_6 (bmPUSLEWID1)
#define PUSLEWIDTH1_3 (bmPULSEWID0 | bmPUSLEWID1)
// (PINCTL)
#define rPINCTL 0x88 //17<<3
#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
// (IOPINS1)
#define rIOPINS1 0xA0 //20<<3
#define bmGPOUT0 0x01 //
#define bmGPOUT1 0x02 //
#define bmGPOUT2 0x04 //
#define bmGPOUT3 0x08 //
#define bmGPIN0 0x10 //
#define bmGPIN1 0x20 //
#define bmGPIN2 0x40 //
#define bmGPIN3 0x80 //
// (IOPINS2)
#define rIOPINS2 0xA8 //21<<3
#define bmGPOUT4 0x01 //
#define bmGPOUT5 0x02 //
#define bmGPOUT6 0x04 //
#define bmGPOUT7 0x08 //
#define bmGPIN4 0x10 //
#define bmGPIN5 0x20 //
#define bmGPIN6 0x40 //
#define bmGPIN7 0x80 //
// (GPINIRQ)
#define rGPINIRQ 0xB0 //22<<3
#define bmGPINIRQ0 0x01 //
#define bmGPINIRQ1 0x02 //
#define bmGPINIRQ2 0x04 //
#define bmGPINIRQ3 0x08 //
#define bmGPINIRQ4 0x10 //
#define bmGPINIRQ5 0x20 //
#define bmGPINIRQ6 0x40 //
#define bmGPINIRQ7 0x80 //
// (GPINIEN)
#define rGPINIEN 0xB8 //23<<3
#define bmGPINIEN0 0x01 //
#define bmGPINIEN1 0x02 //
#define bmGPINIEN2 0x04 //
#define bmGPINIEN3 0x08 //
#define bmGPINIEN4 0x10 //
#define bmGPINIEN5 0x20 //
#define bmGPINIEN6 0x40 //
#define bmGPINIEN7 0x80 //
// (GPINPOL)
#define rGPINPOL 0xC0 //24<<3
#define bmGPINPOL0 0x01 //
#define bmGPINPOL1 0x02 //
#define bmGPINPOL2 0x04 //
#define bmGPINPOL3 0x08 //
#define bmGPINPOL4 0x10 //
#define bmGPINPOL5 0x20 //
#define bmGPINPOL6 0x40 //
#define bmGPINPOL7 0x80 //
//
// If any data transfer errors occur, the HXFRDNIRQ asserts, while the RCVDAVIRQ does not.
//
// The CPU clears the SNDBAVIRQ by writing the SNDBC register.
// The CPU should never directly clear the SNDBAVIRQ bit.
// Host Interrupt Request Status (HIRQ)
#define rHIRQ 0xC8 // Host Interrupt Request Register
#define bmBUSEVENTIRQ 0x01 // BUS Reset Done or BUS Resume Interrupt Request
#define bmRWUIRQ 0x02 // Remote Wakeup Interrupt Request
#define bmRCVDAVIRQ 0x04 // Receive FIFO Data Available Interrupt Request
#define bmSNDBAVIRQ 0x08 // Send Buffer Available Interrupt Request
#define bmSUSDNIRQ 0x10 // Suspend operation Done Interrupt Request
#define bmCONDETIRQ 0x20 // Peripheral Connect/Disconnect Interrupt Request
#define bmFRAMEIRQ 0x40 // Frame Generator Interrupt Request
#define bmHXFRDNIRQ 0x80 // Host Transfer Done Interrupt Request
// IRQs that are OK for the CPU to clear
#define ICLRALLBITS (bmBUSEVENTIRQ | bmRWUIRQ | bmRCVDAVIRQ | bmSUSDNIRQ | bmCONDETIRQ | bmFRAMEIRQ | bmHXFRDNIRQ)
// Host Interrupt Request Control (HIEN)
#define rHIEN 0xD0 //
#define bmBUSEVENTIE bmBUSEVENTIRQ // BUS Reset Done or BUS Resume Interrupt Request Enable
#define bmRWUIE bmRWUIRQ // Remote Wakeup Interrupt Request Enable
#define bmRCVDAVIE bmRCVDAVIRQ // Receive FIFO Data Available Interrupt Request Enable
#define bmSNDBAVIE bmSNDBAVIRQ // Send Buffer Available Interrupt Request Enable
#define bmSUSDNIE bmSUSDNIRQ // Suspend operation Done Interrupt Request Enable
#define bmCONDETIE bmCONDETIRQ // Peripheral Connect/Disconnect Interrupt Request Enable
#define bmFRAMEIE bmFRAMEIRQ // Frame Generator Interrupt Request Enable
#define bmHXFRDNIE bmHXFRDNIRQ // Host Transfer Done Interrupt Request Enable
// (MODE))
#define rMODE 0xD8 //27<<3
#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
// (HCTL)
#define rHCTL 0xE8 //29<<3
#define bmBUSRST 0x01 //
#define bmFRMRST 0x02 //
#define bmSAMPLEBUS 0x04 //
#define bmSIGRSM 0x08 //
#define bmRCVTOG0 0x10 //
#define bmRCVTOG1 0x20 //
#define bmSNDTOG0 0x40 //
#define bmSNDTOG1 0x80 //
// Host transfer (HXFR)
#define rHXFR 0xF0 //30<<3
/* Host transfer token values for writing the HXFR register (R30) */
/* OR this bit field with the endpoint number in bits 3:0 */
#define MAX3421E_tokSETUP 0x10 // HS=0, ISO=0, OUTNIN=0, SETUP=1
#define MAX3421E_tokIN 0x00 // HS=0, ISO=0, OUTNIN=0, SETUP=0
#define MAX3421E_tokOUT 0x20 // HS=0, ISO=0, OUTNIN=1, SETUP=0
#define MAX3421E_tokINHS 0x80 // HS=1, ISO=0, OUTNIN=0, SETUP=0
#define MAX3421E_tokOUTHS 0xA0 // HS=1, ISO=0, OUTNIN=1, SETUP=0
#define MAX3421E_tokISOIN 0x40 // HS=0, ISO=1, OUTNIN=0, SETUP=0
#define MAX3421E_tokISOOUT 0x60 // HS=0, ISO=1, OUTNIN=1, SETUP=0
// (HRSL)
#define rHRSL 0xF8 //31<<3
#define bmRCVTOGRD 0x10 //
#define bmSNDTOGRD 0x20 //
#define bmKSTATUS 0x40 //
#define bmJSTATUS 0x80 //
#define bmSE0 0x00 //SE0 - disconnect state
#define bmSE1 0xC0 //SE1 - illegal state
#define MODE_FS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB)
#define MODE_LS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB)
#endif //_max3421e_h_

View File

@ -0,0 +1,519 @@
/* Copyright (C) 2015-2016 Andrew J. Kroll
and
Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : https://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#ifndef USB_HOST_SHIELD_H
#define USB_HOST_SHIELD_H
// uncomment to get 'printf' console debugging. NOT FOR UNO!
//#define DEBUG_PRINTF_EXTRA_HUGE_USB_HOST_SHIELD
#ifdef LOAD_USB_HOST_SHIELD
#include "UHS_max3421e.h"
#include <SPI.h>
#ifndef SPI_HAS_TRANSACTION
#error "Your SPI library installation is too old."
#else
#ifndef SPI_ATOMIC_VERSION
#warning "Your SPI library installation lacks 'SPI_ATOMIC_VERSION'. Please complain to the maintainer."
#elif SPI_ATOMIC_VERSION < 1
#error "Your SPI library installation is too old."
#endif
#endif
#if DEBUG_PRINTF_EXTRA_HUGE
#ifdef DEBUG_PRINTF_EXTRA_HUGE_USB_HOST_SHIELD
#define MAX_HOST_DEBUG(...) printf_P(__VA_ARGS__)
#else
#define MAX_HOST_DEBUG(...) VOID0
#endif
#else
#define MAX_HOST_DEBUG(...) VOID0
#endif
#ifndef USB_HOST_SHIELD_USE_ISR
#ifdef USE_MULTIPLE_APP_API
#define USB_HOST_SHIELD_USE_ISR 0
#else
#define USB_HOST_SHIELD_USE_ISR 1
#endif
#else
#define USB_HOST_SHIELD_USE_ISR 1
#endif
#if !USB_HOST_SHIELD_USE_ISR
#error NOISR Polled mode _NOT SUPPORTED YET_
//
// Polled defaults
//
#ifdef BOARD_BLACK_WIDDOW
#define UHS_MAX3421E_SS_ 6
#define UHS_MAX3421E_INT_ 3
#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
#if EXT_RAM
// Teensy++ 2.0 with XMEM2
#define UHS_MAX3421E_SS_ 20
#define UHS_MAX3421E_INT_ 7
#else
#define UHS_MAX3421E_SS_ 9
#define UHS_MAX3421E_INT_ 8
#endif
#define UHS_MAX3421E_SPD
#elif defined(ARDUINO_AVR_ADK)
#define UHS_MAX3421E_SS_ 53
#define UHS_MAX3421E_INT_ 54
#elif defined(ARDUINO_AVR_BALANDUINO)
#define UHS_MAX3421E_SS_ 20
#define UHS_MAX3421E_INT_ 19
#else
#define UHS_MAX3421E_SS_ 10
#define UHS_MAX3421E_INT_ 9
#endif
#else
#ifdef ARDUINO_ARCH_PIC32
// PIC32 only allows edge interrupts, isn't that lovely? We'll emulate it...
#if CHANGE < 2
#error core too old.
#endif
#define IRQ_IS_EDGE 0
#ifndef digitalPinToInterrupt
// great, this isn't implemented.
#warning digitalPinToInterrupt is not defined, complain here https://github.com/chipKIT32/chipKIT-core/issues/114
#if defined(_BOARD_UNO_) || defined(_BOARD_UC32_)
#define digitalPinToInterrupt(p) ((p) == 2 ? 1 : ((p) == 7 ? 2 : ((p) == 8 ? 3 : ((p) == 35 ? 4 : ((p) == 38 ? 0 : NOT_AN_INTERRUPT)))))
#warning digitalPinToInterrupt is now defined until this is taken care of.
#else
#error digitalPinToInterrupt not defined for your board, complain here https://github.com/chipKIT32/chipKIT-core/issues/114
#endif
#endif
#else
#define IRQ_IS_EDGE 0
#endif
// More stupidity from our friends @ Sony...
#ifdef ARDUINO_spresense_ast
#ifndef NOT_AN_INTERRUPT
#define NOT_AN_INTERRUPT -1
#endif
#endif
// SAMD uses an enum for this instead of a define. Isn't that just dandy?
#if !defined(NOT_AN_INTERRUPT) && !defined(ARDUINO_ARCH_SAMD)
#warning NOT_AN_INTERRUPT not defined, possible problems ahead.
#warning If NOT_AN_INTERRUPT is an enum or something else, complain to UHS30 developers on github.
#warning Otherwise complain to your board core developer/maintainer.
#define NOT_AN_INTERRUPT -1
#endif
//
// Interrupt defaults. Int0 or Int1
//
#ifdef BOARD_BLACK_WIDDOW
#error "HELP! Please send us an email, I don't know the values for Int0 and Int1 on the Black Widow board!"
#elif defined(ARDUINO_AVR_ADK)
#define UHS_MAX3421E_SS_ 53
#define UHS_MAX3421E_INT_ 54
#elif defined(ARDUINO_spresense_ast)
#define UHS_MAX3421E_SS_ 21
#define UHS_MAX3421E_INT_ 20
#define SPIclass SPI5
//#define UHS_MAX3421E_SPD 100000
#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
// TO-DO!
#if EXT_RAM
// Teensy++ 2.0 with XMEM2
#define UHS_MAX3421E_SS_ 20
#define UHS_MAX3421E_INT_ 7
#else
#define UHS_MAX3421E_SS_ 9
#define UHS_MAX3421E_INT_ 8
#endif
#elif defined(ARDUINO_AVR_BALANDUINO)
#error "ISR mode is currently not supported on the Balanduino. Please set USB_HOST_SHIELD_USE_ISR to 0."
#else
#define UHS_MAX3421E_SS_ 10
#ifdef __AVR__
#ifdef __AVR_ATmega32U4__
#define INT_FOR_PIN2 1
#define INT_FOR_PIN3 0
#else
// Everybody else???
#define INT_FOR_PIN2 0
#define INT_FOR_PIN3 1
#endif
#define UHS_MAX3421E_INT_ 3
#else
// Non-avr
#ifdef ARDUINO_ARCH_PIC32
// UNO32 External Interrupts:
// Pin 38 (INT0), Pin 2 (INT1), Pin 7 (INT2), Pin 8 (INT3), Pin 35 (INT4)
#define UHS_MAX3421E_INT_ 7
#else
#define UHS_MAX3421E_INT_ 9
#endif
#endif
#endif
#endif
#ifdef NO_AUTO_SPEED
// Ugly details section...
// MAX3421E characteristics
// SPI Serial - Clock Input. An external SPI master supplies SCLK with frequencies up to 26MHz. The
// logic level is referenced to the voltage on VL. Data is clocked into the SPI slave inter face on the
// rising edge of SCLK. Data is clocked out of the SPI slave interface on the falling edge of SCLK.
// Serial Clock (SCLK) Period 38.4ns minimum. 17ns minimum pulse width. VL >2.5V
// SCLK Fall to MISO Propagation Delay 14.2ns
// SCLK Fall to MOSI Propagation Delay 14.2ns
// SCLK Fall to MOSI Drive 3.5ns
// Theoretical deadline for reply 17.7ns
// 26MHz 38.4615ns period <-- MAX3421E theoretical maximum
#ifndef UHS_MAX3421E_SPD
#ifdef ARDUINO_SAMD_ZERO
// Zero violates spec early, needs a long setup time, or doesn't like high latency.
#define UHS_MAX3421E_SPD 10000000
#elif defined(ARDUINO_ARCH_PIC32)
// PIC MX 5/6/7 characteristics
// 25MHZ 40ns period <-- PIC MX 5/6/7 theoretical maximum
// pulse width minimum Tsclk/2ns
// Trise/fall 10ns maximum. 5ns is typical but not guaranteed.
// Tsetup minimum for MISO 10ns.
// We are in violation by 7.7ns @ 25MHz due to latency alone.
// Even reading at end of data cycle, we only have a 2.3ns window.
// This is too narrow to to compensate for capacitance, trace lengths, and noise.
// 17.7ns + 10ns = 27.7ns
// 18MHz fits and has enough slack time to compensate for capacitance, trace lengths, and noise.
// For high speeds the SMP bit is recommended too, which samples at the end instead of the middle.
// 20Mhz seems to work.
#define UHS_MAX3421E_SPD 20000000
#else
#define UHS_MAX3421E_SPD 25000000
#endif
#endif
#else
// We start at 25MHz, and back down until hardware can take it.
// Of course, SPI library can adjust this for us too.
// Why not 26MHz? Because I have not found any MCU board that
// can actually go that fast without problems.
// Could be a shield limitation too.
#ifndef UHS_MAX3421E_SPD
#define UHS_MAX3421E_SPD 25000000
#endif
#endif
#ifndef UHS_MAX3421E_INT
#define UHS_MAX3421E_INT UHS_MAX3421E_INT_
#endif
#ifndef UHS_MAX3421E_SS
#define UHS_MAX3421E_SS UHS_MAX3421E_SS_
#endif
// NOTE: On the max3421e the irq enable and irq bits are in the same position.
// IRQs used if CPU polls
#define ENIBITSPOLLED (bmCONDETIE | bmBUSEVENTIE | bmFRAMEIE)
// IRQs used if CPU is interrupted
#define ENIBITSISR (bmCONDETIE | bmBUSEVENTIE | bmFRAMEIE /* | bmRCVDAVIRQ | bmSNDBAVIRQ | bmHXFRDNIRQ */ )
#if !USB_HOST_SHIELD_USE_ISR
#define IRQ_CHECK_MASK (ENIBITSPOLLED & ICLRALLBITS)
#define IRQ_IS_EDGE 0
#else
#define IRQ_CHECK_MASK (ENIBITSISR & ICLRALLBITS)
#endif
#if IRQ_IS_EDGE
// Note: UNO32 Interrupts can only be RISING, or FALLING.
// This poses an interesting problem, since we want to use a LOW level.
// The MAX3421E provides for pulse width control for an IRQ.
// We do need to watch the timing on this, as a second IRQ could cause
// a missed IRQ, since we read the level of the line to check if the IRQ
// is actually for this chip. The only other alternative is to add a capacitor
// and an NPN transistor, and use two lines. We can try this first, though.
// Worse case, we can ignore reading the pin for verification on UNO32.
// Too bad there is no minimum low width setting.
//
// Single Clear First Second Clear first Clear last
// IRQ Single IRQ IRQ Second active pending IRQ
// | | | | | |
// V V V V V V
// _____ _________ _ _ _______
// |______| |______| |______| |______________|
//
#define IRQ_SENSE FALLING
#ifdef ARDUINO_ARCH_PIC32
//#define bmPULSEWIDTH PUSLEWIDTH10_6
#define bmPULSEWIDTH 0
#define bmIRQ_SENSE 0
#else
#define bmPULSEWIDTH PUSLEWIDTH1_3
#define bmIRQ_SENSE 0
#endif
#else
#ifndef IRQ_SENSE
#define IRQ_SENSE LOW
#endif
#ifndef bmPULSEWIDTH
#define bmPULSEWIDTH 0
#endif
#ifndef bmIRQ_SENSE
#define bmIRQ_SENSE bmINTLEVEL
#endif
#endif
class MAX3421E_HOST :
public UHS_USB_HOST_BASE
#ifdef SWI_IRQ_NUM
, public dyn_SWI
#endif
{
// TO-DO: move these into the parent class.
volatile uint8_t vbusState;
volatile uint16_t sof_countdown;
// TO-DO: pack into a struct/union and use one byte
volatile bool busevent;
volatile bool sofevent;
volatile bool counted;
volatile bool condet;
volatile bool doingreset;
#ifdef USB_HOST_MANUAL_POLL
volatile bool frame_irq_enabled = false;
bool enable_frame_irq(bool enable) {
const bool prev_state = frame_irq_enabled;
if(prev_state != enable) {
if(enable)
regWr(rHIEN, regRd(rHIEN) | bmFRAMEIE);
else
regWr(rHIEN, regRd(rHIEN) & ~bmFRAMEIE);
frame_irq_enabled = enable;
}
return prev_state;
}
#endif
public:
SPISettings MAX3421E_SPI_Settings;
uint8_t ss_pin;
uint8_t irq_pin;
// Will use the defaults UHS_MAX3421E_SS, UHS_MAX3421E_INT and speed
UHS_NI MAX3421E_HOST() {
sof_countdown = 0;
busevent = false;
doingreset = false;
sofevent = false;
condet = false;
ss_pin = UHS_MAX3421E_SS;
irq_pin = UHS_MAX3421E_INT;
MAX3421E_SPI_Settings = SPISettings(UHS_MAX3421E_SPD, MSBFIRST, SPI_MODE0);
hub_present = 0;
};
// Will use user supplied pins, and UHS_MAX3421E_SPD
UHS_NI MAX3421E_HOST(uint8_t pss, uint8_t pirq) {
sof_countdown = 0;
busevent = false;
doingreset = false;
sofevent = false;
condet = false;
ss_pin = pss;
irq_pin = pirq;
MAX3421E_SPI_Settings = SPISettings(UHS_MAX3421E_SPD, MSBFIRST, SPI_MODE0);
hub_present = 0;
};
// Will use user supplied pins, and speed
UHS_NI MAX3421E_HOST(uint8_t pss, uint8_t pirq, uint32_t pspd) {
sof_countdown = 0;
doingreset = false;
busevent = false;
sofevent = false;
condet = false;
ss_pin = pss;
irq_pin = pirq;
MAX3421E_SPI_Settings = SPISettings(pspd, MSBFIRST, SPI_MODE0);
hub_present = 0;
};
virtual bool UHS_NI sof_delay(uint16_t x) {
#ifdef USB_HOST_MANUAL_POLL
const bool saved_irq_state = enable_frame_irq(true);
#endif
sof_countdown = x;
while((sof_countdown != 0) && !condet) {
SYSTEM_OR_SPECIAL_YIELD();
#if !USB_HOST_SHIELD_USE_ISR
Task();
#endif
}
#ifdef USB_HOST_MANUAL_POLL
enable_frame_irq(saved_irq_state);
#endif
// Serial.println("...Wake");
return (!condet);
};
virtual UHS_EpInfo *ctrlReqOpen(uint8_t addr, uint64_t Request, uint8_t *dataptr);
virtual void UHS_NI vbusPower(VBUS_t state) {
regWr(rPINCTL, (bmFDUPSPI | bmIRQ_SENSE) | (uint8_t)(state));
};
void UHS_NI Task();
virtual uint8_t SetAddress(uint8_t addr, uint8_t ep, UHS_EpInfo **ppep, uint16_t &nak_limit);
virtual uint8_t OutTransfer(UHS_EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);
virtual uint8_t InTransfer(UHS_EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data);
virtual uint8_t ctrlReqClose(UHS_EpInfo *pep, uint8_t bmReqType, uint16_t left, uint16_t nbytes, uint8_t *dataptr);
virtual uint8_t ctrlReqRead(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint16_t nbytes, uint8_t *dataptr);
virtual uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);
void UHS_NI ReleaseChildren() {
for(uint8_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++)
if(devConfig[i])
devConfig[i]->Release();
hub_present = 0;
};
virtual bool IsHub(uint8_t klass) {
if(klass == UHS_USB_CLASS_HUB) {
hub_present = bmHUBPRE;
return true;
}
return false;
};
virtual void VBUS_changed();
virtual void UHS_NI doHostReset() {
#if USB_HOST_SHIELD_USE_ISR
// Enable interrupts
noInterrupts();
#endif
doingreset = true;
busevent = true;
regWr(rHIRQ, bmBUSEVENTIRQ); // see data sheet.
regWr(rHCTL, bmBUSRST); //issue bus reset
#if USB_HOST_SHIELD_USE_ISR
DDSB();
// Enable interrupts
interrupts();
#endif
while(busevent) {
DDSB();
SYSTEM_OR_SPECIAL_YIELD();
}
#endif
#if USB_HOST_SHIELD_USE_ISR
// Enable interrupts
noInterrupts();
#endif
#ifdef USB_HOST_MANUAL_POLL
enable_frame_irq(true);
#endif
sofevent = true;
#if USB_HOST_SHIELD_USE_ISR
DDSB();
// Enable interrupts
interrupts();
#endif
// Wait for SOF
while(sofevent) {
}
#if USB_HOST_SHIELD_USE_ISR
// Enable interrupts
noInterrupts();
#endif
doingreset = false;
#if USB_HOST_SHIELD_USE_ISR
DDSB();
// Enable interrupts
interrupts();
};
int16_t UHS_NI Init(int16_t mseconds);
int16_t UHS_NI Init() {
return Init(INT16_MIN);
};
void ISRTask();
void ISRbottom();
void busprobe();
uint16_t reset();
// MAX3421e specific
void regWr(uint8_t reg, uint8_t data);
void gpioWr(uint8_t data);
uint8_t regRd(uint8_t reg);
uint8_t gpioRd();
uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t *data_p);
uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t *data_p);
// ARM/NVIC specific, used to emulate reentrant ISR.
#ifdef SWI_IRQ_NUM
void dyn_SWISR() {
ISRbottom();
};
#endif
virtual void UHS_NI suspend_host() {
// Used on MCU that lack control of IRQ priority (AVR).
// Suspends ISRs, for critical code. IRQ will be serviced after it is resumed.
// NOTE: you must track the state yourself!
#ifdef __AVR__
noInterrupts();
detachInterrupt(UHS_GET_DPI(irq_pin));
interrupts();
#endif
};
virtual void UHS_NI resume_host();
};
#ifndef SPIclass
#define SPIclass SPI
#endif
#ifndef USB_HOST_SHIELD_LOADED
#include "USB_HOST_SHIELD_INLINE.h"
#endif
#else
#error "define LOAD_USB_HOST_SHIELD in your sketch, never include USB_HOST_SHIELD.h in a driver."
#endif
#endif /* USB_HOST_SHIELD_H */

View File

@ -0,0 +1,152 @@
/*
* File: macro_logic.h
* Author: root
*
* Created on December 22, 2018, 4:49 AM
*
* To test:
* gcc -DAJK_TEST_MACRO_LOGIC -E macro_logic.h
*/
#ifndef MACRO_LOGIC_H
#define MACRO_LOGIC_H
#define AJK_CAT(a, ...) AJK_PRIMITIVE_CAT(a, __VA_ARGS__)
#define AJK_PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define AJK_COMPL(b) AJK_PRIMITIVE_CAT(AJK_COMPL_, b)
#define AJK_COMPL_0 1
#define AJK_COMPL_1 0
#define AJK_BITAND(x) AJK_PRIMITIVE_CAT(AJK_BITAND_, x)
#define AJK_BITAND_0(y) 0
#define AJK_BITAND_1(y) y
#define AJK_INC(x) AJK_PRIMITIVE_CAT(AJK_INC_, x)
#define AJK_INC_0 1
#define AJK_INC_1 2
#define AJK_INC_2 3
#define AJK_INC_3 4
#define AJK_INC_4 5
#define AJK_INC_5 6
#define AJK_INC_6 7
#define AJK_INC_7 8
#define AJK_INC_8 9
#define AJK_INC_9 10
#define AJK_INC_10 10
#define AJK_DEC(x) AJK_PRIMITIVE_CAT(AJK_DEC_, x)
#define AJK_DEC_0 0
#define AJK_DEC_1 0
#define AJK_DEC_2 1
#define AJK_DEC_3 2
#define AJK_DEC_4 3
#define AJK_DEC_5 4
#define AJK_DEC_6 5
#define AJK_DEC_7 6
#define AJK_DEC_8 7
#define AJK_DEC_9 8
#define AJK_DEC_10 9
#define AJK_CHECK_N(x, n, ...) n
#define AJK_CHECK(...) AJK_CHECK_N(__VA_ARGS__, 0,)
#define AJK_PROBE(x) x, 1,
#define AJK_IS_PAREN(x) AJK_CHECK(AJK_IS_PAREN_PROBE x)
#define AJK_IS_PAREN_PROBE(...) AJK_PROBE(~)
#define AJK_NOT(x) AJK_CHECK(AJK_PRIMITIVE_CAT(AJK_NOT_, x))
#define AJK_NOT_0 AJK_PROBE(~)
#define AJK_COMPL(b) AJK_PRIMITIVE_CAT(AJK_COMPL_, b)
#define AJK_COMPL_0 1
#define AJK_COMPL_1 0
#define AJK_BOOL(x) AJK_COMPL(AJK_NOT(x))
#define AJK_IIF(c) AJK_PRIMITIVE_CAT(AJK_IIF_, c)
#define AJK_IIF_0(t, ...) __VA_ARGS__
#define AJK_IIF_1(t, ...) t
#define AJK_IF(c) AJK_IIF(AJK_BOOL(c))
#define AJK_EAT(...)
#define AJK_EXPAND(...) __VA_ARGS__
#define AJK_WHEN(c) AJK_IF(c)(AJK_EXPAND, AJK_EAT)
#define AJK_EMPTY()
#define AJK_DEFER(id) id AJK_EMPTY()
#define AJK_OBSTRUCT(id) id AJK_DEFER(AJK_EMPTY)()
#define AJK_EVAL(...) AJK_EVAL1(AJK_EVAL1(AJK_EVAL1(__VA_ARGS__)))
#define AJK_EVAL1(...) AJK_EVAL2(AJK_EVAL2(AJK_EVAL2(__VA_ARGS__)))
#define AJK_EVAL2(...) AJK_EVAL3(AJK_EVAL3(AJK_EVAL3(__VA_ARGS__)))
#define AJK_EVAL3(...) AJK_EVAL4(AJK_EVAL4(AJK_EVAL4(__VA_ARGS__)))
#define AJK_EVAL4(...) AJK_EVAL5(AJK_EVAL5(AJK_EVAL5(__VA_ARGS__)))
#define AJK_EVAL5(...) __VA_ARGS__
#define AJK_REPEAT(AJK_count, AJK_macro, ...) \
AJK_WHEN(AJK_count) \
( \
AJK_OBSTRUCT(AJK_REPEAT_INDIRECT) () \
( \
AJK_DEC(AJK_count), AJK_macro, __VA_ARGS__ \
) \
AJK_OBSTRUCT(AJK_macro) \
( \
AJK_DEC(AJK_count), __VA_ARGS__ \
) \
)
#define AJK_REPEAT_INDIRECT() AJK_REPEAT
#define AJK_WHILE(AJK_pred, AJK_op, ...) \
IF(AJK_pred(__VA_ARGS__)) \
( \
AJK_OBSTRUCT(AJK_WHILE_INDIRECT) () \
( \
AJK_pred, AJK_op, AJK_op(__VA_ARGS__) \
), \
__VA_ARGS__ \
)
#define AJK_WHILE_INDIRECT() AJK_WHILE
#define AJK_PRIMITIVE_COMPARE(x, y) AJK_IS_PAREN \
( \
AJK_COMPARE_ ## x ( AJK_COMPARE_ ## y) (()) \
)
#define AJK_IS_COMPARABLE(x) AJK_IS_PAREN( AJK_CAT(AJK_COMPARE_, x) (()) )
#define AJK_NOT_EQUAL(x, y) \
AJK_IIF(AJK_BITAND(AJK_IS_COMPARABLE(x))(AJK_IS_COMPARABLE(y)) ) \
( \
AJK_PRIMITIVE_COMPARE, \
1 AJK_EAT \
)(x, y)
#define AJK_EQUAL(x, y) AJK_COMPL(AJK_NOT_EQUAL(x, y))
#define AJK_COMMA() ,
#define AJK_COMMA_IF(n) AJK_IF(n)(AJK_COMMA, AJK_EAT)()
#define AJK_COMMA_VAR(AJK_count, AJK_v) AJK_COMMA_IF(AJK_count) AJK_v ## AJK_count
#define AJK_MAKE_LIST(AJK_v, AJK_count) AJK_EVAL(AJK_REPEAT(AJK_count, AJK_COMMA_VAR, AJK_v))
#define AJK_FUN(AJK_count, AJK_v, AJK_args, AJK_body) AJK_v ## AJK_count (AJK_args) { AJK_body(AJK_count) }
#define AJK_MAKE_FUNS(AJK_v, AJK_args, AJK_count, AJK_body) AJK_EVAL(AJK_REPEAT(AJK_count, AJK_FUN, AJK_v, AJK_args, AJK_body))
#ifdef AJK_TEST_MACRO_LOGIC
#define BODY(AJKindex) some(C, statement); contaning(a, test[AJKindex]);
#define ZERO_TIMES_TEST 0
#define THREE_TIMES_TEST 3
blank > AJK_MAKE_LIST(VARIABLE_, ZERO_TIMES_TEST) < because zero repeats
Make 3 comma separated indexed variables : AJK_MAKE_LIST(VARIABLE_, THREE_TIMES_TEST)
Make 3 bogus function bodies
AJK_MAKE_FUNS(unsigned Cfunc,(arg1, arg2),3,BODY)
#endif
#endif /* MACRO_LOGIC_H */

View File

@ -0,0 +1,244 @@
/*
* File: SWI_INLINE.h
* Author: xxxajk@gmail.com
*
* Created on December 5, 2014, 9:40 AM
*
* This is the actual library.
* There are no 'c' or 'cpp' files.
*
* 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
*/
#ifdef DYN_SWI_H
#ifndef SWI_INLINE_H
#define SWI_INLINE_H
#ifndef SWI_MAXIMUM_ALLOWED
#define SWI_MAXIMUM_ALLOWED 4
#endif
#if defined(__arm__) || defined(ARDUINO_ARCH_PIC32)
static char dyn_SWI_initied = 0;
static dyn_SWI* dyn_SWI_LIST[SWI_MAXIMUM_ALLOWED];
static dyn_SWI* dyn_SWI_EXEC[SWI_MAXIMUM_ALLOWED];
#ifdef __arm__
#ifdef __USE_CMSIS_VECTORS__
extern "C" {
void (*_VectorsRam[VECTORTABLE_SIZE])(void)__attribute__((aligned(VECTORTABLE_ALIGNMENT)));
}
#else
__attribute__((always_inline)) static inline void __DSB() {
__asm__ volatile ("dsb");
}
#endif // defined(__USE_CMSIS_VECTORS__)
#else // defined(__arm__)
__attribute__((always_inline)) static inline void __DSB() {
__asm__ volatile ("sync" : : : "memory");
}
#endif // defined(__arm__)
/**
* Execute queued class ISR routines.
*/
#ifdef ARDUINO_ARCH_PIC32
static p32_regset *ifs = ((p32_regset *) & IFS0) + (SWI_IRQ_NUM / 32); //interrupt flag register set
static p32_regset *iec = ((p32_regset *) & IEC0) + (SWI_IRQ_NUM / 32); //interrupt enable control reg set
static uint32_t swibit = 1 << (SWI_IRQ_NUM % 32);
void
#ifdef __PIC32MZXX__
__attribute__((nomips16,at_vector(SWI_VECTOR),interrupt(SWI_IPL)))
#else
__attribute__((interrupt(),nomips16))
#endif
softISR() {
#else
#ifdef ARDUINO_spresense_ast
unsigned int softISR() {
#else
void softISR() {
#endif
#endif
//
// TO-DO: Perhaps limit to 8, and inline this?
//
// Make a working copy, while clearing the queue.
noInterrupts();
#ifdef ARDUINO_ARCH_PIC32
//ifs->clr = swibit;
#endif
for(int i = 0; i < SWI_MAXIMUM_ALLOWED; i++) {
dyn_SWI_EXEC[i] = dyn_SWI_LIST[i];
dyn_SWI_LIST[i] = NULL;
}
__DSB();
interrupts();
// Execute each class SWI
for(int i = 0; i < SWI_MAXIMUM_ALLOWED; i++) {
if(dyn_SWI_EXEC[i]) {
#ifdef __DYN_SWI_DEBUG_LED__
digitalWrite(__DYN_SWI_DEBUG_LED__, HIGH);
#endif
dyn_SWI_EXEC[i]->dyn_SWISR();
#ifdef __DYN_SWI_DEBUG_LED__
digitalWrite(__DYN_SWI_DEBUG_LED__, LOW);
#endif
}
}
#ifdef ARDUINO_ARCH_PIC32
noInterrupts();
if(!dyn_SWI_EXEC[0]) ifs->clr = swibit;
interrupts();
#endif
#ifdef ARDUINO_spresense_ast
return 0;
#endif
}
#define DDSB() __DSB()
#endif
#ifdef __arm__
#ifndef interruptsStatus
#define interruptsStatus() __interruptsStatus()
static inline unsigned char __interruptsStatus() __attribute__((always_inline, unused));
static inline unsigned char __interruptsStatus() {
unsigned int primask;
asm volatile ("mrs %0, primask" : "=r" (primask));
if(primask) return 0;
return 1;
}
#endif
/**
* Initialize the Dynamic (class) Software Interrupt
*/
static void Init_dyn_SWI() {
if(!dyn_SWI_initied) {
#ifdef __USE_CMSIS_VECTORS__
uint32_t *X_Vectors = (uint32_t*)SCB->VTOR;
for(int i = 0; i < VECTORTABLE_SIZE; i++) {
_VectorsRam[i] = reinterpret_cast<void (*)()>(X_Vectors[i]); /* copy vector table to RAM */
}
/* relocate vector table */
noInterrupts();
SCB->VTOR = reinterpret_cast<uint32_t>(&_VectorsRam);
DDSB();
interrupts();
#endif
#ifndef ARDUINO_spresense_ast
for(int i = 0; i < SWI_MAXIMUM_ALLOWED; i++) dyn_SWI_LIST[i] = NULL;
noInterrupts();
_VectorsRam[SWI_IRQ_NUM + 16] = reinterpret_cast<void (*)()>(softISR);
DDSB();
interrupts();
NVIC_SET_PRIORITY(SWI_IRQ_NUM, 255);
NVIC_ENABLE_IRQ(SWI_IRQ_NUM);
#endif
#ifdef __DYN_SWI_DEBUG_LED__
pinMode(__DYN_SWI_DEBUG_LED__, OUTPUT);
digitalWrite(__DYN_SWI_DEBUG_LED__, LOW);
#endif
dyn_SWI_initied = 1;
}
}
/**
* @param klass class that extends dyn_SWI
* @return 0 on queue full, else returns queue position (ones based)
*/
int exec_SWI(const dyn_SWI* klass) {
int rc = 0;
uint8_t irestore = interruptsStatus();
// Allow use from inside a critical section...
// ... and prevent races if also used inside an ISR
noInterrupts();
for(int i = 0; i < SWI_MAXIMUM_ALLOWED; i++) {
if(!dyn_SWI_LIST[i]) {
rc = 1 + i; // Success!
dyn_SWI_LIST[i] = (dyn_SWI*)klass;
#ifndef ARDUINO_spresense_ast
if(!NVIC_GET_PENDING(SWI_IRQ_NUM)) NVIC_SET_PENDING(SWI_IRQ_NUM);
#else
// Launch 1-shot timer as an emulated SWI
// Hopefully the value of Zero is legal.
// 1 microsecond latency would suck!
attachTimerInterrupt(softISR, 100);
#endif
DDSB();
break;
}
}
// Restore interrupts, if they were on.
if(irestore) interrupts();
return rc;
}
#elif defined(ARDUINO_ARCH_PIC32)
/**
* Initialize the Dynamic (class) Software Interrupt
*/
static void Init_dyn_SWI() {
if(!dyn_SWI_initied) {
uint32_t sreg = disableInterrupts();
setIntVector(SWI_VECTOR, softISR);
setIntPriority(SWI_VECTOR, 1, 1); // Lowest priority, ever.
ifs->clr = swibit;
iec->clr = swibit;
iec->set = swibit;
restoreInterrupts(sreg);
#ifdef __DYN_SWI_DEBUG_LED__
pinMode(__DYN_SWI_DEBUG_LED__, OUTPUT);
UHS_PIN_WRITE(__DYN_SWI_DEBUG_LED__, LOW);
#endif
}
}
/**
* @param klass class that extends dyn_SWI
* @return 0 on queue full, else returns queue position (ones based)
*/
int exec_SWI(const dyn_SWI* klass) {
int rc = 0;
uint32_t sreg = disableInterrupts();
for(int i = 0; i < SWI_MAXIMUM_ALLOWED; i++) {
if(!dyn_SWI_LIST[i]) {
rc = 1 + i; // Success!
dyn_SWI_LIST[i] = (dyn_SWI*)klass;
if(!(ifs->reg & swibit)) ifs->set = swibit;
;
break;
}
}
restoreInterrupts(sreg);
return rc;
}
#endif /* defined(__arm__) */
#endif /* SWI_INLINE_H */
#else
#error "Never include SWI_INLINE.h directly, include dyn_SWI.h instead"
#endif

View File

@ -0,0 +1,172 @@
/*
* File: dyn_SWI.h
* Author: xxxajk@gmail.com
*
* Created on December 5, 2014, 9:12 AM
*
* 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
*/
#ifndef DYN_SWI_H
#define DYN_SWI_H
#if defined(__arm__) || defined(ARDUINO_ARCH_PIC32)
#ifdef ARDUINO_ARCH_PIC32
#include <p32xxxx.h>
#endif
#ifdef __cplusplus
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif
#endif
#ifdef ARDUINO_spresense_ast
#define SWI_IRQ_NUM 666 // because this board is totally evil.
#elif defined(ARDUINO_ARCH_PIC32)
#ifndef SWI_IRQ_NUM
#ifdef _DSPI0_IPL_ISR
#define SWI_IPL _DSPI0_IPL_ISR
#define SWI_VECTOR _DSPI0_ERR_IRQ
#define SWI_IRQ_NUM _DSPI0_ERR_IRQ
#elif defined(_PMP_ERROR_IRQ)
#define SWI_IRQ_NUM _PMP_ERROR_IRQ
#define SWI_VECTOR _PMP_VECTOR
#else
#error SWI_IRQ_NUM and SWI_VECTOR need a definition
#endif
#ifdef __cplusplus
extern "C"
{
void
#ifdef __PIC32MZXX__
__attribute__((nomips16,at_vector(SWI_VECTOR),interrupt(SWI_IPL)))
#else
__attribute__((interrupt(),nomips16))
#endif
softISR();
}
#endif
#endif
#elif !defined(NVIC_NUM_INTERRUPTS)
// Assume CMSIS
#define __USE_CMSIS_VECTORS__
#ifdef NUMBER_OF_INT_VECTORS
#define NVIC_NUM_INTERRUPTS (NUMBER_OF_INT_VECTORS-16)
#else
#define NVIC_NUM_INTERRUPTS ((int)PERIPH_COUNT_IRQn)
#endif
#define VECTORTABLE_SIZE (NVIC_NUM_INTERRUPTS+16)
#define VECTORTABLE_ALIGNMENT (0x100UL)
#define NVIC_GET_ACTIVE(n) NVIC_GetActive((IRQn_Type)n)
#define NVIC_GET_PENDING(n) NVIC_GetPendingIRQ((IRQn_Type)n)
#define NVIC_SET_PENDING(n) NVIC_SetPendingIRQ((IRQn_Type)n)
#define NVIC_ENABLE_IRQ(n) NVIC_EnableIRQ((IRQn_Type)n)
#define NVIC_SET_PRIORITY(n ,p) NVIC_SetPriority((IRQn_Type)n, (uint32_t) p)
//extern "C" {
// extern uint32_t _VectorsRam[VECTORTABLE_SIZE] __attribute__((aligned(VECTORTABLE_ALIGNMENT)));
//}
#ifndef SWI_IRQ_NUM
#if defined(__SAM3X8E__) && defined(_VARIANT_ARDUINO_DUE_X_)
// DUE
// Choices available:
// HSMCI_IRQn Multimedia Card Interface (HSMCI)
// EMAC_IRQn Ethernet MAC (EMAC)
// EMAC is not broken out on the official DUE, but is on clones.
// SPI0_IRQn Serial Peripheral Interface (SPI0)
// SPI0_IRQn seems to be the best choice, as long as nobody uses an ISR based master
#define SWI_IRQ_NUM SPI0_IRQn
#elif defined(ARDUINO_SAMD_ZERO)
// Just use sercom4's unused IRQ vector.
#define SWI_IRQ_NUM I2S_IRQn
//#define SWI_IRQ_NUM SERCOM4_IRQn
#endif
#endif
#ifndef SWI_IRQ_NUM
#error SWI_IRQ_NUM not defined (CMSIS)
#endif
#elif defined(CORE_TEENSY)
#ifndef NVIC_GET_ACTIVE
#define NVIC_GET_ACTIVE(n) (*((volatile uint32_t *)0xE000E300 + ((n) >> 5)) & (1 << ((n) & 31)))
#endif
#ifndef NVIC_GET_PENDING
#define NVIC_GET_PENDING(n) (*((volatile uint32_t *)0xE000E200 + ((n) >> 5)) & (1 << ((n) & 31)))
#ifndef SWI_IRQ_NUM
#ifdef __MK20DX256__
#define SWI_IRQ_NUM 17
#elif defined(__MK20DX128__)
#define SWI_IRQ_NUM 5
#elif defined(__MKL26Z64__)
#define SWI_IRQ_NUM 4
#elif defined(__MK66FX1M0__)
#define SWI_IRQ_NUM 30
#elif defined(__MK64FX512__)
#define SWI_IRQ_NUM 30
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#define SWI_IRQ_NUM 71
#else
#error Do not know how to relocate IRQ vectors for this pjrc product
#endif
#endif
#endif
#else // Not CMSIS or PJRC CORE_TEENSY or PIC32 or SPRESENSE
#error Do not know how to relocate IRQ vectors or perform SWI
#endif // SWI_IRQ_NUM
#ifndef SWI_IRQ_NUM
#error SWI_IRQ_NUM not defined
#else
/**
* Use this class to extend your class, in order to provide
* a C++ context callable SWI.
*/
class dyn_SWI {
public:
/**
* Override this method with your code.
*/
virtual void dyn_SWISR() {
};
};
extern int exec_SWI(const dyn_SWI* klass);
#include "SWI_INLINE.h"
// IMPORTANT! Define this so that you do NOT end up with a NULL stub!
#define SWI_NO_STUB
#endif /* SWI_IRQ_NUM */
#endif /* __arm__ */
// if no SWI for CPU (e.g. AVR) make a void stub.
#ifndef SWI_NO_STUB
#define Init_dyn_SWI() (void(0))
#ifndef DDSB
#define DDSB() (void(0))
#endif
#endif
#endif /* DYN_SWI_H */