You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
519 lines
16 KiB
519 lines
16 KiB
/* 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 : http://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(void) {
|
|
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(void);
|
|
|
|
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(void) {
|
|
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(void);
|
|
|
|
virtual void UHS_NI doHostReset(void) {
|
|
#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(void) {
|
|
return Init(INT16_MIN);
|
|
};
|
|
|
|
void ISRTask(void);
|
|
void ISRbottom(void);
|
|
void busprobe(void);
|
|
uint16_t reset(void);
|
|
|
|
// MAX3421e specific
|
|
void regWr(uint8_t reg, uint8_t data);
|
|
void gpioWr(uint8_t data);
|
|
uint8_t regRd(uint8_t reg);
|
|
uint8_t gpioRd(void);
|
|
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(void) {
|
|
ISRbottom();
|
|
};
|
|
#endif
|
|
|
|
virtual void UHS_NI suspend_host(void) {
|
|
// 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(void);
|
|
};
|
|
#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 */
|
|
|