12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001 |
- /*
- * 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 publishe7d 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(USB_HOST_SHIELD_LOADED)
- #define USB_HOST_SHIELD_LOADED
-
- #include <Arduino.h>
-
- #ifndef digitalPinToInterrupt
- #error digitalPinToInterrupt not defined, complain to your board maintainer.
- #endif
-
-
- #if USB_HOST_SHIELD_USE_ISR
-
- // allow two slots. this makes the maximum allowed shield count TWO
- // for AVRs this is limited to pins 2 and 3 ONLY
- // for all other boards, one odd and one even pin number is allowed.
- static MAX3421E_HOST *ISReven;
- static MAX3421E_HOST *ISRodd;
-
- static void UHS_NI call_ISReven() {
- ISReven->ISRTask();
- }
-
- static void UHS_NI call_ISRodd() {
- UHS_PIN_WRITE(LED_BUILTIN, HIGH);
- ISRodd->ISRTask();
- }
- #endif
-
-
- void UHS_NI MAX3421E_HOST::resume_host() {
- // Used on MCU that lack control of IRQ priority (AVR).
- // Resumes ISRs.
- // NOTE: you must track the state yourself!
- #ifdef __AVR__
- noInterrupts();
- if (irq_pin & 1) {
- ISRodd = this;
- attachInterrupt(UHS_GET_DPI(irq_pin), call_ISRodd, IRQ_SENSE);
- } else {
- ISReven = this;
- attachInterrupt(UHS_GET_DPI(irq_pin), call_ISReven, IRQ_SENSE);
- }
- interrupts();
- #endif
- }
-
- /* write single byte into MAX3421e register */
- void UHS_NI MAX3421E_HOST::regWr(uint8_t reg, uint8_t data) {
- SPIclass.beginTransaction(MAX3421E_SPI_Settings);
- MARLIN_UHS_WRITE_SS(LOW);
- SPIclass.transfer(reg | 0x02);
- SPIclass.transfer(data);
- MARLIN_UHS_WRITE_SS(HIGH);
- SPIclass.endTransaction();
- }
-
- /* multiple-byte write */
-
- /* returns a pointer to memory position after last written */
- uint8_t* UHS_NI MAX3421E_HOST::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t *data_p) {
- SPIclass.beginTransaction(MAX3421E_SPI_Settings);
- MARLIN_UHS_WRITE_SS(LOW);
- SPIclass.transfer(reg | 0x02);
- //printf("%2.2x :", reg);
-
- while (nbytes) {
- SPIclass.transfer(*data_p);
- //printf("%2.2x ", *data_p);
- nbytes--;
- data_p++; // advance data pointer
- }
- MARLIN_UHS_WRITE_SS(HIGH);
- SPIclass.endTransaction();
- //printf("\r\n");
- 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 UHS_NI MAX3421E_HOST::gpioWr(uint8_t data) {
- regWr(rIOPINS1, data);
- data >>= 4;
- regWr(rIOPINS2, data);
- return;
- }
-
- /* single host register read */
- uint8_t UHS_NI MAX3421E_HOST::regRd(uint8_t reg) {
- SPIclass.beginTransaction(MAX3421E_SPI_Settings);
- MARLIN_UHS_WRITE_SS(LOW);
- SPIclass.transfer(reg);
- uint8_t rv = SPIclass.transfer(0);
- MARLIN_UHS_WRITE_SS(HIGH);
- SPIclass.endTransaction();
- return (rv);
- }
- /* multiple-byte register read */
-
- /* returns a pointer to a memory position after last read */
- uint8_t* UHS_NI MAX3421E_HOST::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t *data_p) {
- SPIclass.beginTransaction(MAX3421E_SPI_Settings);
- MARLIN_UHS_WRITE_SS(LOW);
- SPIclass.transfer(reg);
- while (nbytes) {
- *data_p++ = SPIclass.transfer(0);
- nbytes--;
- }
- MARLIN_UHS_WRITE_SS(HIGH);
- SPIclass.endTransaction();
- return ( data_p);
- }
-
- /* GPIO read. See gpioWr for explanation */
-
- /* GPIN pins are in high nybbles of IOPINS1, IOPINS2 */
- uint8_t UHS_NI MAX3421E_HOST::gpioRd() {
- uint8_t gpin = 0;
- gpin = regRd(rIOPINS2); // pins 4-7
- gpin &= 0xF0; // clean lower nybble
- gpin |= (regRd(rIOPINS1) >> 4); // shift low bits and OR with upper from previous operation.
- return (gpin);
- }
-
- /* reset MAX3421E. Returns number of microseconds it took for PLL to stabilize after reset
- or zero if PLL haven't stabilized in 65535 cycles */
- uint16_t UHS_NI MAX3421E_HOST::reset() {
- uint16_t i = 0;
-
- // Initiate chip reset
- regWr(rUSBCTL, bmCHIPRES);
- regWr(rUSBCTL, 0x00);
-
- int32_t now;
- uint32_t expires = micros() + 65535;
-
- // Enable full-duplex SPI so we can read rUSBIRQ
- regWr(rPINCTL, bmFDUPSPI);
- while ((int32_t)(micros() - expires) < 0L) {
- if ((regRd(rUSBIRQ) & bmOSCOKIRQ)) {
- break;
- }
- }
- now = (int32_t)(micros() - expires);
- if (now < 0L) {
- i = 65535 + now; // Note this subtracts, as now is negative
- }
- return (i);
- }
-
- void UHS_NI MAX3421E_HOST::VBUS_changed() {
- /* modify USB task state because Vbus changed or unknown */
- uint8_t speed = 1;
- //printf("\r\n\r\n\r\n\r\nSTATE %2.2x -> ", usb_task_state);
- switch (vbusState) {
- case LSHOST: // Low speed
- speed = 0;
- // Intentional fall-through
- case FSHOST: // Full speed
- // Start device initialization if we are not initializing
- // Resets to the device cause an IRQ
- // usb_task_state == UHS_USB_HOST_STATE_RESET_NOT_COMPLETE;
- //if ((usb_task_state & UHS_USB_HOST_STATE_MASK) != UHS_USB_HOST_STATE_DETACHED) {
- ReleaseChildren();
- if (!doingreset) {
- if (usb_task_state == UHS_USB_HOST_STATE_RESET_NOT_COMPLETE) {
- usb_task_state = UHS_USB_HOST_STATE_WAIT_BUS_READY;
- } else if (usb_task_state != UHS_USB_HOST_STATE_WAIT_BUS_READY) {
- usb_task_state = UHS_USB_HOST_STATE_DEBOUNCE;
- }
- }
- sof_countdown = 0;
- break;
- case SE1: // illegal state
- sof_countdown = 0;
- doingreset = false;
- ReleaseChildren();
- usb_task_state = UHS_USB_HOST_STATE_ILLEGAL;
- break;
- case SE0: // disconnected
- default:
- sof_countdown = 0;
- doingreset = false;
- ReleaseChildren();
- usb_task_state = UHS_USB_HOST_STATE_IDLE;
- break;
- }
- usb_host_speed = speed;
- //printf("0x%2.2x\r\n\r\n\r\n\r\n", usb_task_state);
- return;
- }
-
- /**
- * Probe bus to determine device presence and speed,
- * then switch host to detected speed.
- */
- void UHS_NI MAX3421E_HOST::busprobe() {
- uint8_t bus_sample;
- uint8_t tmpdata;
- bus_sample = regRd(rHRSL); // Get J,K status
- bus_sample &= (bmJSTATUS | bmKSTATUS); // zero the rest of the byte
- switch (bus_sample) { // start full-speed or low-speed host
- case bmJSTATUS:
- // Serial.println("J");
- 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;
- }
- #ifdef USB_HOST_MANUAL_POLL
- enable_frame_irq(true);
- #endif
- tmpdata = regRd(rMODE) | bmSOFKAENAB; // start SOF generation
- regWr(rHIRQ, bmFRAMEIRQ); // see data sheet.
- regWr(rMODE, tmpdata);
- break;
- case bmKSTATUS:
- // Serial.println("K");
- 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;
- }
- #ifdef USB_HOST_MANUAL_POLL
- enable_frame_irq(true);
- #endif
- tmpdata = regRd(rMODE) | bmSOFKAENAB; // start SOF generation
- regWr(rHIRQ, bmFRAMEIRQ); // see data sheet.
- regWr(rMODE, tmpdata);
- break;
- case bmSE1: // illegal state
- // Serial.println("I");
- regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST);
- vbusState = SE1;
- // sofevent = false;
- break;
- case bmSE0: // disconnected state
- // Serial.println("D");
- regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST);
- vbusState = SE0;
- // sofevent = false;
- break;
- } // end switch ( bus_sample )
- }
-
- /**
- * Initialize USB hardware, turn on VBUS
- *
- * @param mseconds Delay energizing VBUS after mseconds, A value of INT16_MIN means no delay.
- * @return 0 on success, -1 on error
- */
- int16_t UHS_NI MAX3421E_HOST::Init(int16_t mseconds) {
- usb_task_state = UHS_USB_HOST_STATE_INITIALIZE; //set up state machine
- //Serial.print("MAX3421E 'this' USB Host @ 0x");
- //Serial.println((uint32_t)this, HEX);
- //Serial.print("MAX3421E 'this' USB Host Address Pool @ 0x");
- //Serial.println((uint32_t)GetAddressPool(), HEX);
- Init_dyn_SWI();
- UHS_printf_HELPER_init();
- noInterrupts();
- #ifdef ARDUINO_AVR_ADK
- // For Mega ADK, which has a Max3421e on-board,
- // set MAX_RESET to output mode, and then set it to HIGH
- // PORTJ bit 2
- if (irq_pin == 54) {
- DDRJ |= 0x04; // output
- PORTJ |= 0x04; // HIGH
- }
- #endif
- SPIclass.begin();
- #ifdef ARDUINO_AVR_ADK
- if (irq_pin == 54) {
- DDRE &= ~0x20; // input
- PORTE |= 0x20; // pullup
- } else
- #endif
- pinMode(irq_pin, INPUT_PULLUP);
- //UHS_PIN_WRITE(irq_pin, HIGH);
- pinMode(ss_pin, OUTPUT);
- MARLIN_UHS_WRITE_SS(HIGH);
-
- #ifdef USB_HOST_SHIELD_TIMING_PIN
- pinMode(USB_HOST_SHIELD_TIMING_PIN, OUTPUT);
- // My counter/timer can't work on an inverted gate signal
- // so we gate using a high pulse -- AJK
- UHS_PIN_WRITE(USB_HOST_SHIELD_TIMING_PIN, LOW);
- #endif
- interrupts();
-
- #if USB_HOST_SHIELD_USE_ISR
- int intr = digitalPinToInterrupt(irq_pin);
- if (intr == NOT_AN_INTERRUPT) {
- #ifdef ARDUINO_AVR_ADK
- if (irq_pin == 54)
- intr = 6;
- else
- #endif
- return (-2);
- }
- SPIclass.usingInterrupt(intr);
- #else
- SPIclass.usingInterrupt(255);
- #endif
- #ifndef NO_AUTO_SPEED
- // test to get to reset acceptance.
- uint32_t spd = UHS_MAX3421E_SPD;
- again:
- MAX3421E_SPI_Settings = SPISettings(spd, MSBFIRST, SPI_MODE0);
- if (reset() == 0) {
- MAX_HOST_DEBUG(PSTR("Fail SPI speed %lu\r\n"), spd);
- if (spd > 1999999) {
- spd -= 1000000;
- goto again;
- }
- return (-1);
- } else {
- // reset passes, does 64k?
- uint8_t sample_wr = 0;
- uint8_t sample_rd = 0;
- uint8_t gpinpol_copy = regRd(rGPINPOL);
- for (uint16_t j = 0; j < 65535; j++) {
- regWr(rGPINPOL, sample_wr);
- sample_rd = regRd(rGPINPOL);
- if (sample_rd != sample_wr) {
- MAX_HOST_DEBUG(PSTR("Fail SPI speed %lu\r\n"), spd);
- if (spd > 1999999) {
- spd -= 1000000;
- goto again;
- }
- return (-1);
- }
- sample_wr++;
- }
- regWr(rGPINPOL, gpinpol_copy);
- }
-
- MAX_HOST_DEBUG(PSTR("Pass SPI speed %lu\r\n"), spd);
- #endif
-
- if (reset() == 0) { // OSCOKIRQ hasn't asserted in time
- MAX_HOST_DEBUG(PSTR("OSCOKIRQ hasn't asserted in time"));
- return ( -1);
- }
-
- /* MAX3421E - full-duplex SPI, interrupt kind, vbus off */
- regWr(rPINCTL, (bmFDUPSPI | bmIRQ_SENSE | GPX_VBDET));
-
- // 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!
- if (mseconds != INT16_MIN) {
- if (mseconds < 1000) mseconds = 1000;
- delay(mseconds); // We can't depend on SOF timer here.
- }
-
- regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
-
- // Enable interrupts on the MAX3421e
- regWr(rHIEN, IRQ_CHECK_MASK);
- // Enable interrupt pin on the MAX3421e, set pulse width for edge
- regWr(rCPUCTL, (bmIE | bmPULSEWIDTH));
-
- /* check if device is connected */
- regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
- while (!(regRd(rHCTL) & bmSAMPLEBUS)); // wait for sample operation to finish
-
- busprobe(); // check if anything is connected
- VBUS_changed();
-
- // GPX pin on. This is done here so that a change is detected if we have a switch connected.
- /* MAX3421E - full-duplex SPI, interrupt kind, vbus on */
- regWr(rPINCTL, (bmFDUPSPI | bmIRQ_SENSE));
- regWr(rHIRQ, bmBUSEVENTIRQ); // see data sheet.
- regWr(rHCTL, bmBUSRST); // issue bus reset to force generate yet another possible IRQ
-
- #if USB_HOST_SHIELD_USE_ISR
- // Attach ISR to service IRQ from MAX3421e
- noInterrupts();
- if (irq_pin & 1) {
- ISRodd = this;
- attachInterrupt(UHS_GET_DPI(irq_pin), call_ISRodd, IRQ_SENSE);
- } else {
- ISReven = this;
- attachInterrupt(UHS_GET_DPI(irq_pin), call_ISReven, IRQ_SENSE);
- }
- interrupts();
- #endif
- //printf("\r\nrPINCTL 0x%2.2X\r\n", rPINCTL);
- //printf("rCPUCTL 0x%2.2X\r\n", rCPUCTL);
- //printf("rHIEN 0x%2.2X\r\n", rHIEN);
- //printf("irq_pin %i\r\n", irq_pin);
- return 0;
- }
-
- /**
- * Setup UHS_EpInfo structure
- *
- * @param addr USB device address
- * @param ep Endpoint
- * @param ppep pointer to the pointer to a valid UHS_EpInfo structure
- * @param nak_limit how many NAKs before aborting
- * @return 0 on success
- */
- uint8_t UHS_NI MAX3421E_HOST::SetAddress(uint8_t addr, uint8_t ep, UHS_EpInfo **ppep, uint16_t &nak_limit) {
- UHS_Device *p = addrPool.GetUsbDevicePtr(addr);
-
- if (!p)
- return UHS_HOST_ERROR_NO_ADDRESS_IN_POOL;
-
- if (!p->epinfo)
- return UHS_HOST_ERROR_NULL_EPINFO;
-
- *ppep = getEpInfoEntry(addr, ep);
-
- if (!*ppep)
- return UHS_HOST_ERROR_NO_ENDPOINT_IN_TABLE;
-
- nak_limit = (0x0001UL << (((*ppep)->bmNakPower > UHS_USB_NAK_MAX_POWER) ? UHS_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->speed, HEX);
-
- // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise
- regWr(rMODE, (p->speed) ? mode & ~(bmHUBPRE | bmLOWSPEED) : mode | bmLOWSPEED | hub_present);
-
- return 0;
- }
-
- /**
- * Receive a packet
- *
- * @param pep pointer to a valid UHS_EpInfo structure
- * @param nak_limit how many NAKs before aborting
- * @param nbytesptr pointer to maximum number of bytes of data to receive
- * @param data pointer to data buffer
- * @return 0 on success
- */
- uint8_t UHS_NI MAX3421E_HOST::InTransfer(UHS_EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data) {
- uint8_t rcode = 0;
- uint8_t pktsize;
-
- uint16_t nbytes = *nbytesptr;
- MAX_HOST_DEBUG(PSTR("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
- while (1) {
- rcode = dispatchPkt(MAX3421E_tokIN, pep->epAddr, nak_limit); // IN packet to EP-'endpoint'. Function takes care of NAKS.
- #if 0
- // This issue should be resolved now.
- if (rcode == UHS_HOST_ERROR_TOGERR) {
- //MAX_HOST_DEBUG(PSTR("toggle wrong\r\n"));
- // yes, we flip it wrong here so that next time it is actually correct!
- pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
- regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); // set toggle value
- continue;
- }
- #endif
- if (rcode) {
- //MAX_HOST_DEBUG(PSTR(">>>>>>>> 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) {
- //MAX_HOST_DEBUG(PSTR(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n"));
- rcode = 0xF0; // receive error
- break;
- }
- pktsize = regRd(rRCVBC); // number of received bytes
- MAX_HOST_DEBUG(PSTR("Got %i bytes \r\n"), pktsize);
-
- if (pktsize > nbytes) { // certain devices send more than asked
- //MAX_HOST_DEBUG(PSTR(">>>>>>>> Warning: 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)) { // have we transferred 'nbytes' bytes?
- // Save toggle value
- pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0;
- //MAX_HOST_DEBUG(PSTR("\r\n"));
- rcode = 0;
- break;
- } // if
- } // while( 1 )
- return (rcode);
- }
-
- /**
- * Transmit a packet
- *
- * @param pep pointer to a valid UHS_EpInfo structure
- * @param nak_limit how many NAKs before aborting
- * @param nbytes number of bytes of data to send
- * @param data pointer to data buffer
- * @return 0 on success
- */
- uint8_t UHS_NI MAX3421E_HOST::OutTransfer(UHS_EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) {
- uint8_t rcode = UHS_HOST_ERROR_NONE;
- uint8_t retry_count;
- uint8_t *data_p = data; // local copy of the data pointer
- uint16_t bytes_tosend;
- uint16_t nak_count;
- uint16_t bytes_left = nbytes;
-
- uint8_t maxpktsize = pep->maxPktSize;
-
- if (maxpktsize < 1 || maxpktsize > 64)
- return UHS_HOST_ERROR_BAD_MAX_PACKET_SIZE;
-
- unsigned long timeout = millis() + UHS_HOST_TRANSFER_MAX_MS;
-
- regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); // set toggle value
-
- while (bytes_left) {
- SYSTEM_OR_SPECIAL_YIELD();
- 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, (MAX3421E_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 && ((long)(millis() - timeout) < 0L)) {
- switch (rcode) {
- case UHS_HOST_ERROR_NAK:
- nak_count++;
- if (nak_limit && (nak_count == nak_limit))
- goto breakout;
- break;
- case UHS_HOST_ERROR_TIMEOUT:
- retry_count++;
- if (retry_count == UHS_HOST_TRANSFER_RETRY_MAXIMUM)
- goto breakout;
- break;
- case UHS_HOST_ERROR_TOGERR:
- // 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;
- } // switch (rcode
-
- /* process NAK according to Host out NAK bug */
- regWr(rSNDBC, 0);
- regWr(rSNDFIFO, *data_p);
- regWr(rSNDBC, bytes_tosend);
- regWr(rHXFR, (MAX3421E_tokOUT | pep->epAddr)); // dispatch packet
- while (!(regRd(rHIRQ) & bmHXFRDNIRQ)); // wait for the completion IRQ
- regWr(rHIRQ, bmHXFRDNIRQ); // clear IRQ
- rcode = (regRd(rHRSL) & 0x0F);
- SYSTEM_OR_SPECIAL_YIELD();
- } // 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
- }
-
- /**
- * Send the actual packet.
- *
- * @param token
- * @param ep Endpoint
- * @param nak_limit how many NAKs before aborting, 0 == exit after timeout
- * @return 0 on success, 0xFF indicates NAK timeout. @see
- */
- /* 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 UHS_NI MAX3421E_HOST::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) {
- unsigned long timeout = millis() + UHS_HOST_TRANSFER_MAX_MS;
- uint8_t tmpdata;
- uint8_t rcode = UHS_HOST_ERROR_NONE;
- uint8_t retry_count = 0;
- uint16_t nak_count = 0;
-
- for (;;) {
- regWr(rHXFR, (token | ep)); // launch the transfer
- while (long(millis() - timeout) < 0L) { // wait for transfer completion
- SYSTEM_OR_SPECIAL_YIELD();
- tmpdata = regRd(rHIRQ);
-
- if (tmpdata & bmHXFRDNIRQ) {
- regWr(rHIRQ, bmHXFRDNIRQ); // clear the interrupt
- //rcode = 0x00;
- break;
- } // if (tmpdata & bmHXFRDNIRQ
-
- } // while (millis() < timeout
-
- rcode = (regRd(rHRSL) & 0x0F); // analyze transfer result
-
- switch (rcode) {
- case UHS_HOST_ERROR_NAK:
- nak_count++;
- if (nak_limit && (nak_count == nak_limit))
- return (rcode);
- delayMicroseconds(200);
- break;
- case UHS_HOST_ERROR_TIMEOUT:
- retry_count++;
- if (retry_count == UHS_HOST_TRANSFER_RETRY_MAXIMUM)
- return (rcode);
- break;
- default:
- return (rcode);
- } // switch (rcode)
- }
- }
-
- //
- // NULL is error, we don't need to know the reason.
- //
-
- UHS_EpInfo * UHS_NI MAX3421E_HOST::ctrlReqOpen(uint8_t addr, uint64_t Request, uint8_t *dataptr) {
- uint8_t rcode;
- UHS_EpInfo *pep = NULL;
- uint16_t nak_limit = 0;
- rcode = SetAddress(addr, 0, &pep, nak_limit);
-
- if (!rcode) {
-
- bytesWr(rSUDFIFO, 8, (uint8_t*)(&Request)); // transfer to setup packet FIFO
-
- rcode = dispatchPkt(MAX3421E_tokSETUP, 0, nak_limit); // dispatch packet
- if (!rcode) {
- if (dataptr != NULL) {
- if (((Request)/* bmReqType*/ & 0x80) == 0x80) {
- pep->bmRcvToggle = 1; //bmRCVTOG1;
- } else {
- pep->bmSndToggle = 1; //bmSNDTOG1;
- }
- }
- } else {
- pep = NULL;
- }
- }
- return pep;
- }
-
- uint8_t UHS_NI MAX3421E_HOST::ctrlReqRead(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint16_t nbytes, uint8_t *dataptr) {
- *read = 0;
- uint16_t nak_limit = 0;
- MAX_HOST_DEBUG(PSTR("ctrlReqRead left: %i\r\n"), *left);
- if (*left) {
- again:
- *read = nbytes;
- uint8_t rcode = InTransfer(pep, nak_limit, read, dataptr);
- if (rcode == UHS_HOST_ERROR_TOGERR) {
- // yes, we flip it wrong here so that next time it is actually correct!
- pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
- goto again;
- }
-
- if (rcode) {
- MAX_HOST_DEBUG(PSTR("ctrlReqRead ERROR: %2.2x, left: %i, read %i\r\n"), rcode, *left, *read);
- return rcode;
- }
- *left -= *read;
- MAX_HOST_DEBUG(PSTR("ctrlReqRead left: %i, read %i\r\n"), *left, *read);
- }
- return 0;
- }
-
- uint8_t UHS_NI MAX3421E_HOST::ctrlReqClose(UHS_EpInfo *pep, uint8_t bmReqType, uint16_t left, uint16_t nbytes, uint8_t *dataptr) {
- uint8_t rcode = 0;
-
- //MAX_HOST_DEBUG(PSTR("Closing"));
- if (((bmReqType & 0x80) == 0x80) && pep && left && dataptr) {
- MAX_HOST_DEBUG(PSTR("ctrlReqRead Sinking %i\r\n"), left);
- // If reading, sink the rest of the data.
- while (left) {
- uint16_t read = nbytes;
- rcode = InTransfer(pep, 0, &read, dataptr);
- if (rcode == UHS_HOST_ERROR_TOGERR) {
- // yes, we flip it wrong here so that next time it is actually correct!
- pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
- continue;
- }
- if (rcode) break;
- left -= read;
- if (read < nbytes) break;
- }
- }
- if (!rcode) {
- //Serial.println("Dispatching");
- rcode = dispatchPkt(((bmReqType & 0x80) == 0x80) ? MAX3421E_tokOUTHS : MAX3421E_tokINHS, 0, 0); //GET if direction
- //} else {
- //Serial.println("Bypassed Dispatch");
- }
- return rcode;
- }
-
- /**
- * Bottom half of the ISR task
- */
- void UHS_NI MAX3421E_HOST::ISRbottom() {
- uint8_t x;
- // Serial.print("Enter ");
- // Serial.print((uint32_t)this,HEX);
- // Serial.print(" ");
- // Serial.println(usb_task_state, HEX);
-
- DDSB();
- if (condet) {
- VBUS_changed();
- #if USB_HOST_SHIELD_USE_ISR
- noInterrupts();
- #endif
- condet = false;
- #if USB_HOST_SHIELD_USE_ISR
- interrupts();
- #endif
- }
- switch (usb_task_state) {
- case UHS_USB_HOST_STATE_INITIALIZE:
- // should never happen...
- MAX_HOST_DEBUG(PSTR("UHS_USB_HOST_STATE_INITIALIZE\r\n"));
- busprobe();
- VBUS_changed();
- break;
- case UHS_USB_HOST_STATE_DEBOUNCE:
- MAX_HOST_DEBUG(PSTR("UHS_USB_HOST_STATE_DEBOUNCE\r\n"));
- // This seems to not be needed. The host controller has debounce built in.
- sof_countdown = UHS_HOST_DEBOUNCE_DELAY_MS;
- usb_task_state = UHS_USB_HOST_STATE_DEBOUNCE_NOT_COMPLETE;
- break;
- case UHS_USB_HOST_STATE_DEBOUNCE_NOT_COMPLETE:
- MAX_HOST_DEBUG(PSTR("UHS_USB_HOST_STATE_DEBOUNCE_NOT_COMPLETE\r\n"));
- if (!sof_countdown) usb_task_state = UHS_USB_HOST_STATE_RESET_DEVICE;
- break;
- case UHS_USB_HOST_STATE_RESET_DEVICE:
- MAX_HOST_DEBUG(PSTR("UHS_USB_HOST_STATE_RESET_DEVICE\r\n"));
- busevent = true;
- usb_task_state = UHS_USB_HOST_STATE_RESET_NOT_COMPLETE;
- regWr(rHIRQ, bmBUSEVENTIRQ); // see data sheet.
- regWr(rHCTL, bmBUSRST); // issue bus reset
- break;
- case UHS_USB_HOST_STATE_RESET_NOT_COMPLETE:
- MAX_HOST_DEBUG(PSTR("UHS_USB_HOST_STATE_RESET_NOT_COMPLETE\r\n"));
- if (!busevent) usb_task_state = UHS_USB_HOST_STATE_WAIT_BUS_READY;
- break;
- case UHS_USB_HOST_STATE_WAIT_BUS_READY:
- MAX_HOST_DEBUG(PSTR("UHS_USB_HOST_STATE_WAIT_BUS_READY\r\n"));
- usb_task_state = UHS_USB_HOST_STATE_CONFIGURING;
- break; // don't fall through
-
- case UHS_USB_HOST_STATE_CONFIGURING:
- usb_task_state = UHS_USB_HOST_STATE_CHECK;
- x = Configuring(0, 1, usb_host_speed);
- usb_error = x;
- if (usb_task_state == UHS_USB_HOST_STATE_CHECK) {
- if (x) {
- MAX_HOST_DEBUG(PSTR("Error 0x%2.2x"), x);
- if (x == UHS_HOST_ERROR_JERR) {
- usb_task_state = UHS_USB_HOST_STATE_IDLE;
- } else if (x != UHS_HOST_ERROR_DEVICE_INIT_INCOMPLETE) {
- usb_error = x;
- usb_task_state = UHS_USB_HOST_STATE_ERROR;
- }
- } else
- usb_task_state = UHS_USB_HOST_STATE_CONFIGURING_DONE;
- }
- break;
-
- case UHS_USB_HOST_STATE_CHECK:
- // Serial.println((uint32_t)__builtin_return_address(0), HEX);
- break;
- case UHS_USB_HOST_STATE_CONFIGURING_DONE:
- usb_task_state = UHS_USB_HOST_STATE_RUNNING;
- break;
-
- #ifdef USB_HOST_MANUAL_POLL
- case UHS_USB_HOST_STATE_RUNNING:
- case UHS_USB_HOST_STATE_ERROR:
- case UHS_USB_HOST_STATE_IDLE:
- case UHS_USB_HOST_STATE_ILLEGAL:
- enable_frame_irq(false);
- break;
- #else
- case UHS_USB_HOST_STATE_RUNNING:
- Poll_Others();
- for (x = 0; (usb_task_state == UHS_USB_HOST_STATE_RUNNING) && (x < UHS_HOST_MAX_INTERFACE_DRIVERS); x++) {
- if (devConfig[x]) {
- if (devConfig[x]->bPollEnable) devConfig[x]->Poll();
- }
- }
- // fall thru
- #endif
- default:
- // Do nothing
- break;
- } // switch ( usb_task_state )
- DDSB();
- #if USB_HOST_SHIELD_USE_ISR
- if (condet) {
- VBUS_changed();
- noInterrupts();
- condet = false;
- interrupts();
- }
- #endif
- #ifdef USB_HOST_SHIELD_TIMING_PIN
- // My counter/timer can't work on an inverted gate signal
- // so we gate using a high pulse -- AJK
- UHS_PIN_WRITE(USB_HOST_SHIELD_TIMING_PIN, LOW);
- #endif
- //usb_task_polling_disabled--;
- EnablePoll();
- DDSB();
- }
-
-
- /* USB main task. Services the MAX3421e */
- #if !USB_HOST_SHIELD_USE_ISR
- void UHS_NI MAX3421E_HOST::ISRTask() {}
- void UHS_NI MAX3421E_HOST::Task()
- #else
- void UHS_NI MAX3421E_HOST::Task() {
- #ifdef USB_HOST_MANUAL_POLL
- if (usb_task_state == UHS_USB_HOST_STATE_RUNNING) {
- noInterrupts();
- for (uint8_t x = 0; x < UHS_HOST_MAX_INTERFACE_DRIVERS; x++)
- if (devConfig[x] && devConfig[x]->bPollEnable)
- devConfig[x]->Poll();
- interrupts();
- }
- #endif
- }
-
- void UHS_NI MAX3421E_HOST::ISRTask()
- #endif
- {
- DDSB();
-
- #ifndef SWI_IRQ_NUM
- suspend_host();
- #if USB_HOST_SHIELD_USE_ISR
- // Enable interrupts
- interrupts();
- #endif
- #endif
-
- counted = false;
- if (!MARLIN_UHS_READ_IRQ()) {
- uint8_t HIRQALL = regRd(rHIRQ); // determine interrupt source
- uint8_t HIRQ = HIRQALL & IRQ_CHECK_MASK;
- uint8_t HIRQ_sendback = 0x00;
-
- if ((HIRQ & bmCONDETIRQ) || (HIRQ & bmBUSEVENTIRQ)) {
- MAX_HOST_DEBUG
- (PSTR("\r\nBEFORE CDIRQ %s BEIRQ %s resetting %s state 0x%2.2x\r\n"),
- (HIRQ & bmCONDETIRQ) ? "T" : "F",
- (HIRQ & bmBUSEVENTIRQ) ? "T" : "F",
- doingreset ? "T" : "F",
- usb_task_state
- );
- }
- // ALWAYS happens BEFORE or WITH CONDETIRQ
- if (HIRQ & bmBUSEVENTIRQ) {
- HIRQ_sendback |= bmBUSEVENTIRQ;
- if (!doingreset) condet = true;
- busprobe();
- busevent = false;
- }
-
- if (HIRQ & bmCONDETIRQ) {
- HIRQ_sendback |= bmCONDETIRQ;
- if (!doingreset) condet = true;
- busprobe();
- }
-
- #if 1
- if ((HIRQ & bmCONDETIRQ) || (HIRQ & bmBUSEVENTIRQ)) {
- MAX_HOST_DEBUG
- (PSTR("\r\nAFTER CDIRQ %s BEIRQ %s resetting %s state 0x%2.2x\r\n"),
- (HIRQ & bmCONDETIRQ) ? "T" : "F",
- (HIRQ & bmBUSEVENTIRQ) ? "T" : "F",
- doingreset ? "T" : "F",
- usb_task_state
- );
- }
- #endif
-
- if (HIRQ & bmFRAMEIRQ) {
- HIRQ_sendback |= bmFRAMEIRQ;
- if (sof_countdown) {
- sof_countdown--;
- counted = true;
- }
- sofevent = false;
- }
-
- //MAX_HOST_DEBUG(PSTR("\r\n%s%s%s\r\n"),
- // sof_countdown ? "T" : "F",
- // counted ? "T" : "F",
- // usb_task_polling_disabled? "T" : "F");
- DDSB();
- regWr(rHIRQ, HIRQ_sendback);
- #ifndef SWI_IRQ_NUM
- resume_host();
- #if USB_HOST_SHIELD_USE_ISR
- // Disable interrupts
- noInterrupts();
- #endif
- #endif
- if (!sof_countdown && !counted && !usb_task_polling_disabled) {
- DisablePoll();
- //usb_task_polling_disabled++;
- #ifdef USB_HOST_SHIELD_TIMING_PIN
- // My counter/timer can't work on an inverted gate signal
- // so we gate using a high pulse -- AJK
- UHS_PIN_WRITE(USB_HOST_SHIELD_TIMING_PIN, HIGH);
- #endif
-
- #ifdef SWI_IRQ_NUM
- //MAX_HOST_DEBUG(PSTR("--------------- Doing SWI ----------------"));
- exec_SWI(this);
- #else
- #if USB_HOST_SHIELD_USE_ISR
- // Enable interrupts
- interrupts();
- #endif
- ISRbottom();
- #endif /* SWI_IRQ_NUM */
- }
- }
- }
-
- #if 0
- DDSB();
- #endif
-
- #else
- #error "Never include USB_HOST_SHIELD_INLINE.h, include UHS_host.h instead"
- #endif
|