Skip to content

Instantly share code, notes, and snippets.

@blindman2k
Created July 2, 2014 20:31
Show Gist options
  • Save blindman2k/f4da0312c3f09df91347 to your computer and use it in GitHub Desktop.
Save blindman2k/f4da0312c3f09df91347 to your computer and use it in GitHub Desktop.
First attempt at a driver for the MAX3421E USB Host
// -----------------------------------------------------------------------------
// 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