Created
July 2, 2014 20:31
-
-
Save blindman2k/f4da0312c3f09df91347 to your computer and use it in GitHub Desktop.
First attempt at a driver for the MAX3421E USB Host
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----------------------------------------------------------------------------- | |
// USB Host Shield pinouts | |
// | |
// 1- CS (not sure if it's active high/low) | |
// 2- MISO | |
// 5- SCLK | |
// 7- MOSI | |
// 8- RESET (also not sure about active high/low) | |
// 9- Interrupt (not sure if this is useful for anything) | |
function max_constants() { | |
// From max3421e.h | |
/* MAX3421E command byte format: rrrrr0wa where 'r' is register number */ | |
// | |
// MAX3421E Registers in HOST mode. | |
// | |
const rRCVFIFO = 0x08 //1<<3 | |
const rSNDFIFO = 0x10 //2<<3 | |
const rSUDFIFO = 0x20 //4<<3 | |
const rRCVBC = 0x30 //6<<3 | |
const rSNDBC = 0x38 //7<<3 | |
const rUSBIRQ = 0x68 //13<<3 | |
/* USBIRQ Bits */ | |
const bmVBUSIRQ = 0x40 //b6 | |
const bmNOVBUSIRQ = 0x20 //b5 | |
const bmOSCOKIRQ = 0x01 //b0 | |
const rUSBIEN = 0x70 //14<<3 | |
/* USBIEN Bits */ | |
const bmVBUSIE = 0x40 //b6 | |
const bmNOVBUSIE = 0x20 //b5 | |
const bmOSCOKIE = 0x01 //b0 | |
const rUSBCTL = 0x78 //15<<3 | |
/* USBCTL Bits */ | |
const bmCHIPRES = 0x20 //b5 | |
const bmPWRDOWN = 0x10 //b4 | |
const rCPUCTL = 0x80 //16<<3 | |
/* CPUCTL Bits */ | |
const bmPUSLEWID1 = 0x80 //b7 | |
const bmPULSEWID0 = 0x40 //b6 | |
const bmIE = 0x01 //b0 | |
const rPINCTL = 0x88 //17<<3 | |
/* PINCTL Bits */ | |
const bmFDUPSPI = 0x10 //b4 | |
const bmINTLEVEL = 0x08 //b3 | |
const bmPOSINT = 0x04 //b2 | |
const bmGPXB = 0x02 //b1 | |
const bmGPXA = 0x01 //b0 | |
// GPX pin selections | |
const GPX_OPERATE = 0x00 | |
const GPX_VBDET = 0x01 | |
const GPX_BUSACT = 0x02 | |
const GPX_SOF = 0x03 | |
const rREVISION = 0x90 //18<<3 | |
const rIOPINS1 = 0xa0 //20<<3 | |
/* IOPINS1 Bits */ | |
const bmGPOUT0 = 0x01 | |
const bmGPOUT1 = 0x02 | |
const bmGPOUT2 = 0x04 | |
const bmGPOUT3 = 0x08 | |
const bmGPIN0 = 0x10 | |
const bmGPIN1 = 0x20 | |
const bmGPIN2 = 0x40 | |
const bmGPIN3 = 0x80 | |
const rIOPINS2 = 0xa8 //21<<3 | |
/* IOPINS2 Bits */ | |
const bmGPOUT4 = 0x01 | |
const bmGPOUT5 = 0x02 | |
const bmGPOUT6 = 0x04 | |
const bmGPOUT7 = 0x08 | |
const bmGPIN4 = 0x10 | |
const bmGPIN5 = 0x20 | |
const bmGPIN6 = 0x40 | |
const bmGPIN7 = 0x80 | |
const rGPINIRQ = 0xb0 //22<<3 | |
/* GPINIRQ Bits */ | |
const bmGPINIRQ0 = 0x01 | |
const bmGPINIRQ1 = 0x02 | |
const bmGPINIRQ2 = 0x04 | |
const bmGPINIRQ3 = 0x08 | |
const bmGPINIRQ4 = 0x10 | |
const bmGPINIRQ5 = 0x20 | |
const bmGPINIRQ6 = 0x40 | |
const bmGPINIRQ7 = 0x80 | |
const rGPINIEN = 0xb8 //23<<3 | |
/* GPINIEN Bits */ | |
const bmGPINIEN0 = 0x01 | |
const bmGPINIEN1 = 0x02 | |
const bmGPINIEN2 = 0x04 | |
const bmGPINIEN3 = 0x08 | |
const bmGPINIEN4 = 0x10 | |
const bmGPINIEN5 = 0x20 | |
const bmGPINIEN6 = 0x40 | |
const bmGPINIEN7 = 0x80 | |
const rGPINPOL = 0xc0 //24<<3 | |
/* GPINPOL Bits */ | |
const bmGPINPOL0 = 0x01 | |
const bmGPINPOL1 = 0x02 | |
const bmGPINPOL2 = 0x04 | |
const bmGPINPOL3 = 0x08 | |
const bmGPINPOL4 = 0x10 | |
const bmGPINPOL5 = 0x20 | |
const bmGPINPOL6 = 0x40 | |
const bmGPINPOL7 = 0x80 | |
const rHIRQ = 0xc8 //25<<3 | |
/* HIRQ Bits */ | |
const bmBUSEVENTIRQ = 0x01 // indicates BUS Reset Done or BUS Resume | |
const bmRWUIRQ = 0x02 | |
const bmRCVDAVIRQ = 0x04 | |
const bmSNDBAVIRQ = 0x08 | |
const bmSUSDNIRQ = 0x10 | |
const bmCONDETIRQ = 0x20 | |
const bmFRAMEIRQ = 0x40 | |
const bmHXFRDNIRQ = 0x80 | |
const rHIEN = 0xd0 //26<<3 | |
/* HIEN Bits */ | |
const bmBUSEVENTIE = 0x01 | |
const bmRWUIE = 0x02 | |
const bmRCVDAVIE = 0x04 | |
const bmSNDBAVIE = 0x08 | |
const bmSUSDNIE = 0x10 | |
const bmCONDETIE = 0x20 | |
const bmFRAMEIE = 0x40 | |
const bmHXFRDNIE = 0x80 | |
const rMODE = 0xd8 //27<<3 | |
/* MODE Bits */ | |
const bmHOST = 0x01 | |
const bmLOWSPEED = 0x02 | |
const bmHUBPRE = 0x04 | |
const bmSOFKAENAB = 0x08 | |
const bmSEPIRQ = 0x10 | |
const bmDELAYISO = 0x20 | |
const bmDMPULLDN = 0x40 | |
const bmDPPULLDN = 0x80 | |
const rPERADDR = 0xe0 //28<<3 | |
const rHCTL = 0xe8 //29<<3 | |
/* HCTL Bits */ | |
const bmBUSRST = 0x01 | |
const bmFRMRST = 0x02 | |
const bmSAMPLEBUS = 0x04 | |
const bmSIGRSM = 0x08 | |
const bmRCVTOG0 = 0x10 | |
const bmRCVTOG1 = 0x20 | |
const bmSNDTOG0 = 0x40 | |
const bmSNDTOG1 = 0x80 | |
const 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 */ | |
const tokSETUP = 0x10 // HS=0, ISO=0, OUTNIN=0, SETUP=1 | |
const tokIN = 0x00 // HS=0, ISO=0, OUTNIN=0, SETUP=0 | |
const tokOUT = 0x20 // HS=0, ISO=0, OUTNIN=1, SETUP=0 | |
const tokINHS = 0x80 // HS=1, ISO=0, OUTNIN=0, SETUP=0 | |
const tokOUTHS = 0xA0 // HS=1, ISO=0, OUTNIN=1, SETUP=0 | |
const tokISOIN = 0x40 // HS=0, ISO=1, OUTNIN=0, SETUP=0 | |
const tokISOOUT = 0x60 // HS=0, ISO=1, OUTNIN=1, SETUP=0 | |
const rHRSL = 0xf8 //31<<3 | |
/* HRSL Bits */ | |
const bmRCVTOGRD = 0x10 | |
const bmSNDTOGRD = 0x20 | |
const bmKSTATUS = 0x40 | |
const bmJSTATUS = 0x80 | |
const bmSE0 = 0x00 //SE0 - disconnect state | |
const bmSE1 = 0xc0 //SE1 - illegal state | |
/* Host error result codes, the 4 LSB's in the HRSL register */ | |
const hrSUCCESS = 0x00 | |
const hrBUSY = 0x01 | |
const hrBADREQ = 0x02 | |
const hrUNDEF = 0x03 | |
const hrNAK = 0x04 | |
const hrSTALL = 0x05 | |
const hrTOGERR = 0x06 | |
const hrWRONGPID = 0x07 | |
const hrBADBC = 0x08 | |
const hrPIDERR = 0x09 | |
const hrPKTERR = 0x0A | |
const hrCRCERR = 0x0B | |
const hrKERR = 0x0C | |
const hrJERR = 0x0D | |
const hrTIMEOUT = 0x0E | |
const hrBABBLE = 0x0F | |
// I am not sure what these are yet | |
const SE0 = 0 | |
const SE1 = 1 | |
const FSHOST = 2 | |
const LSHOST = 3 | |
} | |
function usb_constants() { | |
// From usb_ch9.h | |
/* Misc.USB constants */ | |
const DEV_DESCR_LEN = 18 //device descriptor length | |
const CONF_DESCR_LEN = 9 //configuration descriptor length | |
const INTR_DESCR_LEN = 9 //interface descriptor length | |
const EP_DESCR_LEN = 7 //endpoint descriptor length | |
/* Standard Device Requests */ | |
const USB_REQUEST_GET_STATUS = 0 // Standard Device Request - GET STATUS | |
const USB_REQUEST_CLEAR_FEATURE = 1 // Standard Device Request - CLEAR FEATURE | |
const USB_REQUEST_SET_FEATURE = 3 // Standard Device Request - SET FEATURE | |
const USB_REQUEST_SET_ADDRESS = 5 // Standard Device Request - SET ADDRESS | |
const USB_REQUEST_GET_DESCRIPTOR = 6 // Standard Device Request - GET DESCRIPTOR | |
const USB_REQUEST_SET_DESCRIPTOR = 7 // Standard Device Request - SET DESCRIPTOR | |
const USB_REQUEST_GET_CONFIGURATION = 8 // Standard Device Request - GET CONFIGURATION | |
const USB_REQUEST_SET_CONFIGURATION = 9 // Standard Device Request - SET CONFIGURATION | |
const USB_REQUEST_GET_INTERFACE = 10 // Standard Device Request - GET INTERFACE | |
const USB_REQUEST_SET_INTERFACE = 11 // Standard Device Request - SET INTERFACE | |
const USB_REQUEST_SYNCH_FRAME = 12 // Standard Device Request - SYNCH FRAME | |
const USB_FEATURE_ENDPOINT_HALT = 0 // CLEAR/SET FEATURE - Endpoint Halt | |
const USB_FEATURE_DEVICE_REMOTE_WAKEUP = 1 // CLEAR/SET FEATURE - Device remote wake-up | |
const USB_FEATURE_TEST_MODE = 2 // CLEAR/SET FEATURE - Test mode | |
/* Setup Data Constants */ | |
const USB_SETUP_HOST_TO_DEVICE = 0x00 // Device Request bmRequestType transfer direction - host to device transfer | |
const USB_SETUP_DEVICE_TO_HOST = 0x80 // Device Request bmRequestType transfer direction - device to host transfer | |
const USB_SETUP_TYPE_STANDARD = 0x00 // Device Request bmRequestType type - standard | |
const USB_SETUP_TYPE_CLASS = 0x20 // Device Request bmRequestType type - class | |
const USB_SETUP_TYPE_VENDOR = 0x40 // Device Request bmRequestType type - vendor | |
const USB_SETUP_RECIPIENT_DEVICE = 0x00 // Device Request bmRequestType recipient - device | |
const USB_SETUP_RECIPIENT_INTERFACE = 0x01 // Device Request bmRequestType recipient - interface | |
const USB_SETUP_RECIPIENT_ENDPOINT = 0x02 // Device Request bmRequestType recipient - endpoint | |
const USB_SETUP_RECIPIENT_OTHER = 0x03 // Device Request bmRequestType recipient - other | |
/* USB descriptors */ | |
const USB_DESCRIPTOR_DEVICE = 0x01 // bDescriptorType for a Device Descriptor. | |
const USB_DESCRIPTOR_CONFIGURATION = 0x02 // bDescriptorType for a Configuration Descriptor. | |
const USB_DESCRIPTOR_STRING = 0x03 // bDescriptorType for a String Descriptor. | |
const USB_DESCRIPTOR_INTERFACE = 0x04 // bDescriptorType for an Interface Descriptor. | |
const USB_DESCRIPTOR_ENDPOINT = 0x05 // bDescriptorType for an Endpoint Descriptor. | |
const USB_DESCRIPTOR_DEVICE_QUALIFIER = 0x06 // bDescriptorType for a Device Qualifier. | |
const USB_DESCRIPTOR_OTHER_SPEED = 0x07 // bDescriptorType for a Other Speed Configuration. | |
const USB_DESCRIPTOR_INTERFACE_POWER = 0x08 // bDescriptorType for Interface Power. | |
const USB_DESCRIPTOR_OTG = 0x09 // bDescriptorType for an OTG Descriptor. | |
const HID_DESCRIPTOR_HID = 0x21 | |
/* OTG SET FEATURE Constants */ | |
const OTG_FEATURE_B_HNP_ENABLE = 3 // SET FEATURE OTG - Enable B device to perform HNP | |
const OTG_FEATURE_A_HNP_SUPPORT = 4 // SET FEATURE OTG - A device supports HNP | |
const OTG_FEATURE_A_ALT_HNP_SUPPORT = 5 // SET FEATURE OTG - Another port on the A device supports HNP | |
/* USB Endpoint Transfer Types */ | |
const USB_TRANSFER_TYPE_CONTROL = 0x00 // Endpoint is a control endpoint. | |
const USB_TRANSFER_TYPE_ISOCHRONOUS = 0x01 // Endpoint is an isochronous endpoint. | |
const USB_TRANSFER_TYPE_BULK = 0x02 // Endpoint is a bulk endpoint. | |
const USB_TRANSFER_TYPE_INTERRUPT = 0x03 // Endpoint is an interrupt endpoint. | |
const bmUSB_TRANSFER_TYPE = 0x03 // bit mask to separate transfer type from ISO attributes | |
/* Standard Feature Selectors for CLEAR_FEATURE Requests */ | |
const USB_FEATURE_ENDPOINT_STALL = 0 // Endpoint recipient | |
const USB_FEATURE_DEVICE_REMOTE_WAKEUP = 1 // Device recipient | |
const USB_FEATURE_TEST_MODE = 2 // Device recipient | |
// From UsbCore.h | |
/* Common setup data constant combinations */ | |
// bmREQ_GET_DESCR = USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //get descriptor request type | |
// 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' | |
// 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 | |
const USB_CLASS_USE_CLASS_INFO = 0x00 // Use Class Info in the Interface Descriptors | |
const USB_CLASS_AUDIO = 0x01 // Audio | |
const USB_CLASS_COM_AND_CDC_CTRL = 0x02 // Communications and CDC Control | |
const USB_CLASS_HID = 0x03 // HID | |
const USB_CLASS_PHYSICAL = 0x05 // Physical | |
const USB_CLASS_IMAGE = 0x06 // Image | |
const USB_CLASS_PRINTER = 0x07 // Printer | |
const USB_CLASS_MASS_STORAGE = 0x08 // Mass Storage | |
const USB_CLASS_HUB = 0x09 // Hub | |
const USB_CLASS_CDC_DATA = 0x0a // CDC-Data | |
const USB_CLASS_SMART_CARD = 0x0b // Smart-Card | |
const USB_CLASS_CONTENT_SECURITY = 0x0d // Content Security | |
const USB_CLASS_VIDEO = 0x0e // Video | |
const USB_CLASS_PERSONAL_HEALTH = 0x0f // Personal Healthcare | |
const USB_CLASS_DIAGNOSTIC_DEVICE = 0xdc // Diagnostic Device | |
const USB_CLASS_WIRELESS_CTRL = 0xe0 // Wireless Controller | |
const USB_CLASS_MISC = 0xef // Miscellaneous | |
const USB_CLASS_APP_SPECIFIC = 0xfe // Application Specific | |
const USB_CLASS_VENDOR_SPECIFIC = 0xff // Vendor Specific | |
// Additional Error Codes | |
const USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED = 0xD1 | |
const USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE = 0xD2 | |
const USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS = 0xD3 | |
const USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL = 0xD4 | |
const USB_ERROR_HUB_ADDRESS_OVERFLOW = 0xD5 | |
const USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL = 0xD6 | |
const USB_ERROR_EPINFO_IS_NULL = 0xD7 | |
const USB_ERROR_INVALID_ARGUMENT = 0xD8 | |
const USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE = 0xD9 | |
const USB_ERROR_INVALID_MAX_PKT_SIZE = 0xDA | |
const USB_ERROR_EP_NOT_FOUND_IN_TBL = 0xDB | |
const USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET = 0xE0 | |
const USB_ERROR_FailGetDevDescr = 0xE1 | |
const USB_ERROR_FailSetDevTblEntry = 0xE2 | |
const USB_ERROR_FailGetConfDescr = 0xE3 | |
const USB_ERROR_TRANSFER_TIMEOUT = 0xFF | |
const USB_XFER_TIMEOUT = 10000 //30000 // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec | |
//const USB_NAK_LIMIT = 32000 //NAK limit for a transfer. 0 means NAKs are not counted | |
const USB_RETRY_LIMIT = 3 // 3 retry limit for a transfer | |
const USB_SETTLE_DELAY = 200 //settle delay in milliseconds | |
const USB_NUMDEVICES = 16 //number of USB devices | |
//const HUB_MAX_HUBS = 7 // maximum number of hubs that can be attached to the host controller | |
const HUB_PORT_RESET_DELAY = 20 // hub port reset delay 10 ms recomended, can be up to 20 ms | |
/* USB state machine states */ | |
const USB_STATE_MASK = 0xf0 | |
const USB_STATE_DETACHED = 0x10 | |
const USB_DETACHED_SUBSTATE_INITIALIZE = 0x11 | |
const USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE = 0x12 | |
const USB_DETACHED_SUBSTATE_ILLEGAL = 0x13 | |
const USB_ATTACHED_SUBSTATE_SETTLE = 0x20 | |
const USB_ATTACHED_SUBSTATE_RESET_DEVICE = 0x30 | |
const USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE = 0x40 | |
const USB_ATTACHED_SUBSTATE_WAIT_SOF = 0x50 | |
const USB_ATTACHED_SUBSTATE_WAIT_RESET = 0x51 | |
const USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE = 0x60 | |
const USB_STATE_ADDRESSING = 0x70 | |
const USB_STATE_CONFIGURING = 0x80 | |
const USB_STATE_RUNNING = 0x90 | |
const USB_STATE_ERROR = 0xa0 | |
// Made just for Squirrel from usb_ch9.h | |
const USB_DEVICE_DESCRIPTOR_SIZE = 18; | |
} | |
// ----------------------------------------------------------------------------- | |
class MAX3421E { | |
_spi = null; | |
_cs_l = null; | |
_rst_l = null; | |
_int = null; | |
_cb_connected = null; | |
_running = false; | |
constructor(spi, cs_l, rst_l, int) { | |
_spi = spi; | |
_cs_l = cs_l; | |
_rst_l = rst_l; | |
_int = int; | |
_spi.configure(CLOCK_IDLE_LOW | MSB_FIRST, 15000); | |
_cs_l.configure(DIGITAL_OUT); _cs_l.write(1); | |
_rst_l.configure(DIGITAL_OUT); _rst_l.write(1); | |
_int.configure(DIGITAL_IN, interrupt.bindenv(this)); | |
max_constants(); | |
hard_reset(); | |
init(); | |
} | |
function hard_reset() { | |
_rst_l.write(0); | |
imp.sleep(0.1); | |
_rst_l.write(1); | |
} | |
function regRd(register, bytes = 1) { | |
_cs_l.write(0); | |
_spi.write(register.tochar()); | |
local readBlob = _spi.readblob(bytes); | |
_cs_l.write(1); | |
if (bytes == 1) return readBlob[0]; | |
else return readBlob; | |
} | |
function bytesRd(register, bytes = 1) { | |
return regRd(register, bytes); | |
} | |
function regWr(register, data) { | |
local result = null; | |
_cs_l.write(0); | |
_spi.write((register | 0x02).tochar()); | |
if (typeof data == "integer") { | |
result = _spi.write(data.tochar()) == 1; | |
} else { | |
result = _spi.write(data) == data.len(); | |
} | |
_cs_l.write(1); | |
return result; | |
} | |
function init() { | |
assert(regWr(rPINCTL, bmFDUPSPI | bmPOSINT)); // Full duplix SPI, edge-active, rising edges | |
assert(chip_reset() != 0); | |
assert(regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST)); // set pull-downs, Host | |
assert(regWr(rHIEN, bmCONDETIE | bmFRAMEIE)); //connection detection | |
/* check if device is connected */ | |
assert(regWr(rHCTL, bmSAMPLEBUS)); // sample USB bus | |
while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish | |
local probe_result = busprobe(); | |
regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt | |
regWr(rCPUCTL, 0x01); //enable interrupt pin | |
// Force the interrupt callback if the device is already connected | |
if (probe_result == LSHOST || probe_result == FSHOST) { | |
imp.wakeup(0, function() { | |
if (_cb_connected) { | |
busreset(probe_result == LSHOST); | |
_cb_connected(true); | |
} | |
}.bindenv(this)) | |
} | |
} | |
function chip_reset() { | |
assert(regWr(rUSBCTL, bmCHIPRES)); | |
assert(regWr(rUSBCTL, 0x00)); | |
local i = 0; | |
while (++i) { | |
if ((regRd(rUSBIRQ) & bmOSCOKIRQ)) { | |
break; | |
} | |
} | |
return i; | |
} | |
function busprobe() { | |
local vbusState = null; | |
local bus_sample = regRd(rHRSL); //Get J,K status | |
bus_sample = bus_sample & (bmJSTATUS | bmKSTATUS); //zero the rest of the byte | |
switch (bus_sample) { //start full-speed or low-speed host | |
case bmJSTATUS: | |
if((regRd(rMODE) & bmLOWSPEED) == 0) { | |
regWr(rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB); //start full-speed host | |
vbusState = FSHOST; | |
} else { | |
regWr(rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB); //start low-speed host | |
vbusState = LSHOST; | |
} | |
break; | |
case bmKSTATUS: | |
if((regRd(rMODE) & bmLOWSPEED) == 0) { | |
regWr(rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB); //start low-speed host | |
vbusState = LSHOST; | |
} else { | |
regWr(rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB); //start full-speed host | |
vbusState = FSHOST; | |
} | |
break; | |
case bmSE1: //illegal state | |
vbusState = SE1; | |
server.error("Detected illegal host state"); | |
break; | |
case bmSE0: //disconnected state | |
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ); | |
vbusState = SE0; | |
break; | |
default: | |
server.error("Unknown bus state: " + bus_sample); | |
} | |
return vbusState; | |
} | |
function busreset(lowspeed) { | |
// Let the bus settle | |
imp.sleep(USB_SETTLE_DELAY / 1000.0); | |
//issue bus reset | |
regWr(rHCTL, bmBUSRST); | |
// Wait for a response | |
while ((regRd(rHCTL) & bmBUSRST) != 0); | |
local tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation | |
regWr(rMODE, tmpdata); | |
// when first SOF received _and_ 20ms has passed we can continue | |
while ((regRd(rHIRQ) & bmFRAMEIRQ) == 0); | |
imp.sleep(0.02); | |
// Configure | |
configuring(0, 0, lowspeed); | |
} | |
function interrupt(force = false) { | |
// Rising edge | |
if (force || _int.read() == 1) { | |
local probe_result = null; | |
local HIRQ_sendback = 0; | |
local HIRQ = regRd(rHIRQ); //determine interrupt source | |
if (HIRQ & bmCONDETIRQ) { | |
probe_result = busprobe(); | |
HIRQ_sendback = HIRQ_sendback | bmCONDETIRQ; | |
} | |
// End HIRQ interrupts handling, clear serviced IRQs | |
regWr(rHIRQ, HIRQ_sendback); | |
// Call the registered callback if the state has changed | |
local state_change = (probe_result != null); | |
local connected = (probe_result != 0); | |
local lowspeed = (probe_result == LSHOST); | |
if (state_change && _cb_connected) { | |
if (connected != _running) { | |
busreset(lowspeed); | |
_cb_connected(connected); | |
} | |
if (!connected) _running = false; | |
} | |
} | |
} | |
function revision() { | |
local rev = regRd(rREVISION); | |
return rev; | |
} | |
function configuring(parent, port, lowspeed) { | |
local desc = getDevDescr(0, 0) | |
_running = (desc != null); | |
} | |
function on_connected(callback) { | |
_cb_connected = callback; | |
} | |
} | |
// ----------------------------------------------------------------------------- | |
class USB extends MAX3421E { | |
bmRcvToggle = 1; // NOTE: This should be per end point, not global | |
// These should be constants | |
bmREQ_GET_DESCR = USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //get descriptor request type | |
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' | |
bmREQ_CL_GET_INTF = USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE //get interface request type | |
constructor(spi, cs_l, rst_l, int) { | |
base.constructor(spi, cs_l, rst_l, int); | |
usb_constants(); | |
} | |
function init() { | |
base.init(); | |
} | |
function SetAddress(addr, ep) { | |
//set peripheral address | |
regWr(rPERADDR, addr); | |
// Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise | |
local mode = regRd(rMODE); | |
local lowspeed = (mode & 0x02) == 0x02; | |
local bmHubPre = 0; | |
regWr(rMODE, lowspeed ? (mode | bmLOWSPEED | bmHubPre) : (mode & ~(bmHUBPRE | bmLOWSPEED))); | |
} | |
function ctrlReq(addr, ep, bmReqType, bRequest, wValLo, wValHi, wInd, nBytes) { | |
// Control transfer. Sets address, endpoint, fills control packet with necessary data, | |
// dispatches control packet, and initiates bulk IN transfer, depending on request. | |
SetAddress(addr, ep); | |
local direction = (bmReqType & 0x80) != 0; | |
/* fill in setup packet */ | |
local setup_pkt = format("%c%c%c%c%c%c%c%c", | |
bmReqType & 0xFF, | |
bRequest & 0xFF, | |
wValLo & 0xFF, | |
wValHi & 0xFF, | |
(wInd >> 8) & 0xFF, wInd & 0xFF, | |
(nBytes >> 8) & 0xFF, nBytes & 0xFF); | |
assert(regWr(rSUDFIFO, setup_pkt)); //transfer to setup packet FIFO | |
// dispatch packet | |
if (dispatchPkt(tokSETUP, ep) != 0) return false; | |
if (direction) { | |
local data = InTransfer(addr, ep, nBytes) | |
if (data && data.len() == nBytes) { | |
local dbg = ""; | |
foreach (ch in data) { | |
dbg += format("%02X ", ch) | |
} | |
server.log("ctrlReq:InTransfer = " + dbg); | |
return data; | |
} | |
} else { | |
server.log("Sending data out") | |
} | |
} | |
function dispatchPkt(token, ep) { | |
local nak_limit = 5; // This should be calculated in SetAddress | |
local rcode = 0, nak_count = 0, retry_count = 0;; | |
local timeout = hardware.millis() + USB_XFER_TIMEOUT; | |
while (hardware.millis() < timeout) { | |
//launch the transfer | |
assert(regWr(rHXFR, (token | ep))); | |
rcode = USB_ERROR_TRANSFER_TIMEOUT; | |
//wait for confirmation of arrival | |
while (hardware.millis() < timeout) { | |
if (regRd(rHIRQ) & bmHXFRDNIRQ) { | |
//clear the interrupt | |
regWr(rHIRQ, bmHXFRDNIRQ); | |
rcode = 0x00; | |
break; | |
} | |
} | |
// Abort if we have a timeout | |
if (rcode == USB_ERROR_TRANSFER_TIMEOUT) { | |
return rcode; | |
} | |
//analyze the result | |
rcode = regRd(rHRSL) & 0x0F; | |
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; | |
} | |
} | |
return rcode; | |
} | |
function InTransfer(addr, ep, nBytes) { | |
local rcode = SetAddress(addr, ep); | |
local data = blob(); | |
//set initial toggle value | |
regWr(rHCTL, bmRcvToggle ? bmRCVTOG1 : bmRCVTOG0); | |
while (true) { | |
//IN packet to EP-'endpoint'. | |
local epAddr = 0; | |
rcode = dispatchPkt(tokIN, epAddr); | |
if (rcode == hrTOGERR) { | |
// yes, we flip it wrong here so that next time it is actually correct! | |
bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; | |
regWr(rHCTL, bmRcvToggle ? bmRCVTOG1 : bmRCVTOG0); //set toggle value | |
continue; | |
} else if (rcode > 0) { | |
server.log(format("InTransfer:dispatchPkt returned 0x%02X", rcode)) | |
break; | |
} | |
/* 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) { | |
rcode = 0xf0; //receive error | |
break; | |
} | |
// number of bytes in the receive buffer | |
local pktsize = regRd(rRCVBC); | |
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. | |
pktsize = nBytes; | |
} | |
// Read the data and add it to the buffer | |
local newdata = bytesRd(rRCVFIFO, pktsize); | |
data.writeblob(newdata); | |
regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer | |
/* The transfer is complete under two conditions: */ | |
/* 1. The device sent a short packet (L.T. maxPacketSize) */ | |
/* 2. 'nbytes' have been transferred. */ | |
if (data.len() >= nBytes) // have we transferred 'nbytes' bytes? | |
{ | |
// Save toggle value | |
bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0; | |
rcode = 0; | |
break; | |
} | |
} | |
server.log(format("InTransfer: result 0x%02X, received %d out of %d bytes", rcode, data.len(), nBytes)); | |
if (rcode == 0) return data; | |
else return null; | |
} | |
function getDevDescr(addr, ep) { | |
local buf = ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, USB_DEVICE_DESCRIPTOR_SIZE); | |
if (typeof buf != "blob") return null; | |
if (buf.len() != USB_DEVICE_DESCRIPTOR_SIZE) return null; | |
local devdescr = {}; | |
devdescr.bLength <- buf[0]; // Length of this descriptor. | |
devdescr.bDescriptorType <- buf[1]; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE). | |
devdescr.bcdUSB <- (buf[2] << 8) | (buf[3]); // USB Spec Release Number (BCD). | |
devdescr.bDeviceClass <- buf[4]; // Class code (assigned by the USB-IF). 0xFF-Vendor specific. | |
devdescr.bDeviceSubClass <- buf[5]; // Subclass code (assigned by the USB-IF). | |
devdescr.bDeviceProtocol <- buf[6]; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific. | |
devdescr.bMaxPacketSize0 <- buf[7]; // Maximum packet size for endpoint 0. | |
devdescr.idVendor <- (buf[8] << 8) | (buf[9]); // Vendor ID (assigned by the USB-IF). | |
devdescr.idProduct <- (buf[10] << 8) | (buf[11]); // Product ID (assigned by the manufacturer). | |
devdescr.bcdDevice <- (buf[12] << 8) | (buf[13]); // Device release number (BCD). | |
devdescr.iManufacturer <- buf[14]; // Index of String Descriptor describing the manufacturer. | |
devdescr.iProduct <- buf[15]; // Index of String Descriptor describing the product. | |
devdescr.iSerialNumber <- buf[16]; // Index of String Descriptor with the device's serial number. | |
devdescr.bNumConfigurations <- buf[17]; // Number of possible configurations. | |
foreach (k,v in devdescr) { | |
server.log(format("devdescr.%s = 0x%02x", k, v)) | |
} | |
return devdescr; | |
} | |
} | |
// ----------------------------------------------------------------------------- | |
class HIDBoot { | |
_usb = null; | |
constructor(usb) { | |
_usb = usb; | |
_usb.on_connected(onconnected.bindenv(this)) | |
} | |
function onconnected(connected) { | |
if (connected) { | |
server.log("Connected"); | |
} else { | |
server.log("Disconnected"); | |
} | |
} | |
} | |
// ----------------------------------------------------------------------------- | |
spi <- hardware.spi257; | |
cs <- hardware.pin1; | |
reset <- hardware.pin8; | |
int <- hardware.pin9; | |
usb <- USB(spi, cs, reset, int); | |
keyboard <- HIDBoot(usb); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment