/** * Performs an in transfer from a USB device from an arbitrary endpoint. * * @param device USB bulk device. * @param device length number of bytes to read. * @param data target buffer. * @return number of bytes read, or error code in case of failure. */ int16_t USB::read( usb_device * device, usb_endpoint * endpoint, uint16_t length, uint8_t *data, uint16_t nakLimit ) { int8_t rcode; int8_t bytesRead; uint16_t maxPacketSize = endpoint->maxPacketSize; uint16_t totalTransferred = 0; // Set device address. max3421e_write( MAX_REG_PERADDR, device->address ); // Set toggle value. max3421e_write( MAX_REG_HCTL, endpoint->receiveToggle ); while( 1 ) { // Start IN transfer rcode = USB::dispatchPacket( tokIN, endpoint, nakLimit ); if( rcode ) { // if( rcode != hrNAK ) serialPrintf( "USB::read: dispatch error %d\n", rcode ); return -1; } // Assert that the RCVDAVIRQ bit in register MAX_REG_HIRQ is set. if( (max3421e_read( MAX_REG_HIRQ ) & bmRCVDAVIRQ) == 0 ) { // serialPrintf("USB::read: toggle error? %d\n", rcode); // TODO: the absence of RCVDAVIRQ indicates a toggle error. Need to add handling for that. return -2; } // Obtain the number of bytes in FIFO. bytesRead = max3421e_read( MAX_REG_RCVBC ); // Read the data from the FIFO. data = max3421e_readMultiple( MAX_REG_RCVFIFO, bytesRead, data ); // Clear the interrupt to free the buffer. max3421e_write(MAX_REG_HIRQ, bmRCVDAVIRQ); totalTransferred += bytesRead; // Check if we're done reading. Either we've received a 'short' packet (<maxPacketSize), or the // desired number of bytes has been transferred. if( (bytesRead < maxPacketSize) || (totalTransferred >= length) ) { // Remember the toggle value for the next transfer. if (max3421e_read(MAX_REG_HRSL) & bmRCVTOGRD) endpoint->receiveToggle = bmRCVTOG1; else endpoint->receiveToggle = bmRCVTOG0; // Break out of the loop. break; } } // Report success. return totalTransferred; }
/** * Interrupt handler. */ uint8_t max3421e_interruptHandler(void) { uint8_t interruptStatus; uint8_t HIRQ_sendback = 0x00; // Determine interrupt source. interruptStatus = max3421e_read(MAX_REG_HIRQ); if (interruptStatus & bmFRAMEIRQ) { //->1ms SOF interrupt handler HIRQ_sendback |= bmFRAMEIRQ; } if (interruptStatus & bmCONDETIRQ) { max3421e_busprobe(); HIRQ_sendback |= bmCONDETIRQ; } // End HIRQ interrupts handling, clear serviced IRQs max3421e_write(MAX_REG_HIRQ, HIRQ_sendback); return (HIRQ_sendback); }
/** * Probes the bus to determine device presence and speed, and switches host to this speed. */ void max3421e_busprobe(void) { uint8_t bus_sample; bus_sample = max3421e_read(MAX_REG_HRSL); //Get J,K status bus_sample &= (bmJSTATUS | bmKSTATUS); //zero the rest of the uint8_t switch (bus_sample) { //start full-speed or low-speed host case (bmJSTATUS): if ((max3421e_read(MAX_REG_MODE) & bmLOWSPEED) == 0) { max3421e_write(MAX_REG_MODE, MODE_FS_HOST ); //start full-speed host vbusState = FSHOST; } else { max3421e_write(MAX_REG_MODE, MODE_LS_HOST); //start low-speed host vbusState = LSHOST; } break; case (bmKSTATUS): if ((max3421e_read(MAX_REG_MODE) & bmLOWSPEED) == 0) { max3421e_write(MAX_REG_MODE, MODE_LS_HOST ); //start low-speed host vbusState = LSHOST; } else { max3421e_write(MAX_REG_MODE, MODE_FS_HOST ); //start full-speed host vbusState = FSHOST; } break; case (bmSE1): //illegal state vbusState = SE1; break; case (bmSE0): //disconnected state vbusState = SE0; break; } }
/** * Resets the max3412e. Sets the chip reset bit, SPI configuration is not affected. * @return true iff success. */ boolean max3421e_reset(void) { uint8_t tmp = 0; // Chip reset. This stops the oscillator max3421e_write(MAX_REG_USBCTL, bmCHIPRES); // Remove the reset max3421e_write(MAX_REG_USBCTL, 0x00); delay(10); // Wait until the PLL is stable while (!(max3421e_read(MAX_REG_USBIRQ) & bmOSCOKIRQ)) { // Timeout after 256 attempts. tmp++; if (tmp == 0) return (false); } // Success. return (true); }
/** * Initialises the max3421e after power-on. */ void max3421e_powerOn(void) { // Configure full-duplex SPI, interrupt pulse. max3421e_write(MAX_REG_PINCTL, (bmFDUPSPI + bmINTLEVEL + bmGPXB)); //Full-duplex SPI, level interrupt, GPX // Stop/start the oscillator. if (max3421e_reset() == false) Serial.print("Error: OSCOKIRQ failed to assert\n"); // Configure host operation. max3421e_write(MAX_REG_MODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ ); // set pull-downs, Host, Separate GPIN IRQ on GPX max3421e_write(MAX_REG_HIEN, bmCONDETIE | bmFRAMEIE ); //connection detection // Check if device is connected. max3421e_write(MAX_REG_HCTL, bmSAMPLEBUS ); // sample USB bus while (!(max3421e_read(MAX_REG_HCTL) & bmSAMPLEBUS)); //wait for sample operation to finish max3421e_busprobe(); //check if anything is connected max3421e_write(MAX_REG_HIRQ, bmCONDETIRQ ); //clear connection detect interrupt // Enable interrupt pin. max3421e_write(MAX_REG_CPUCTL, 0x01); }
int usb_dispatchPacket(uint8_t token, usb_endpoint * endpoint, unsigned int nakLimit) { // uint32_t timeout = avr_millis() + USB_XFER_TIMEOUT; uint16_t timeout = xTaskGetTickCount() + USB_XFER_TIMEOUT / portTICK_RATE_MS; // XXX from freeRTOS uint8_t tmpdata; uint8_t rcode = 0; unsigned int nak_count = 0; char retry_count = 0; while (timeout > xTaskGetTickCount() ) { // Analyze transfer result. // Launch the transfer. max3421e_write(MAX_REG_HXFR, (token | endpoint->address)); rcode = 0xff; // Wait for interrupt while (timeout > xTaskGetTickCount() ) { tmpdata = max3421e_read(MAX_REG_HIRQ); if (tmpdata & bmHXFRDNIRQ) { // Clear the interrupt. max3421e_write(MAX_REG_HIRQ, bmHXFRDNIRQ); rcode = 0x00; break; } } // Exit if timeout. if (rcode != 0x00) return (rcode); // Wait for HRSL while (timeout > xTaskGetTickCount() ) { rcode = (max3421e_read(MAX_REG_HRSL) & 0x0f); if (rcode != hrBUSY) break; else xSerialPrint_P(PSTR("USB dispatch busy!\r\n")); } switch (rcode) { case hrNAK: nak_count++; if (nak_count == nakLimit) return (rcode); break; case hrTIMEOUT: retry_count++; if (retry_count == USB_RETRY_LIMIT) return (rcode); break; default: return (rcode); } } return (rcode); }
/** * Sends a control request to a USB device. * * @param device USB device to send the control request to. * @param requestType request type (in/out). * @param request request. * @param valueLow low byte of the value parameter. * @param valueHigh high byte of the value parameter. * @param index index. * @param length number of bytes to transfer. * @param data data to send in case of output transfer, or reception buffer in case of input. If no data is to be exchanged this should be set to NULL. * @return 0 on success, error code otherwise */ int usb_controlRequest( usb_device * device, uint8_t requestType, uint8_t request, uint8_t valueLow, uint8_t valueHigh, uint16_t index, uint16_t length, uint8_t * data) { boolean direction = false; //request direction, IN or OUT uint8_t rcode; usb_setupPacket setup_pkt; // Set device address. max3421e_write(MAX_REG_PERADDR, device->address); if (requestType & 0x80) direction = true; //determine request direction // Build setup packet. setup_pkt.bmRequestType = requestType; setup_pkt.bRequest = request; setup_pkt.wValue = valueLow | (valueHigh << 8); setup_pkt.wIndex = index; setup_pkt.wLength = length; // Write setup packet to the FIFO and dispatch max3421e_writeMultiple(MAX_REG_SUDFIFO, 8, (uint8_t *) &setup_pkt); rcode = usb_dispatchPacket(tokSETUP, &(device->control), USB_NAK_LIMIT); // Print error in case of failure. if (rcode) { xSerialPrintf_P(PSTR("Setup packet error: 0x%02x @ Tick: %u\r\n"), rcode, xTaskGetTickCount()); return -1; } // Data stage, if present if (data != NULL) { rcode = usb_ctrlData(device, direction, length, data); // If unsuccessful, return error. if (rcode<0) { xSerialPrintf_P(PSTR("\r\nData packet error: 0x%02x @ Tick: %u\r\n"), rcode, xTaskGetTickCount()); return -2; } } // Status stage. if (direction) rcode = usb_dispatchPacket(tokOUTHS, &(device->control), USB_NAK_LIMIT); else rcode = usb_dispatchPacket(tokINHS, &(device->control), USB_NAK_LIMIT); if (rcode) return -3; else return 0; }
/** * Performs ab out transfer to a USB device on an arbitrary endpoint. * * @param device USB bulk device. * @param device length number of bytes to read. * @param data target buffer. * @return number of bytes written, or error code in case of failure. */ int usb_write(usb_device * device, usb_endpoint * endpoint, uint16_t length, uint8_t * data) { uint8_t rcode = 0, retry_count; // Set device address. max3421e_write(MAX_REG_PERADDR, device->address); // Local copy of the data pointer. uint8_t * data_p = data; unsigned int bytes_tosend, nak_count; unsigned int bytes_left = length; unsigned int nak_limit = USB_NAK_LIMIT; // uint32_t timeout = avr_millis() + USB_XFER_TIMEOUT; // XXX from freeRTOS uint16_t timeout = xTaskGetTickCount() + USB_XFER_TIMEOUT / portTICK_RATE_MS; uint8_t maxPacketSize = endpoint->maxPacketSize; // If maximum packet size is not set, return. if (!maxPacketSize) return 0xFE; max3421e_write(MAX_REG_HCTL, endpoint->sendToggle); //set toggle value while (bytes_left) { retry_count = 0; nak_count = 0; bytes_tosend = (bytes_left >= maxPacketSize) ? maxPacketSize : bytes_left; // Filling output FIFO max3421e_writeMultiple(MAX_REG_SNDFIFO, bytes_tosend, data_p); // Set number of bytes to send. max3421e_write(MAX_REG_SNDBC, bytes_tosend); // Dispatch packet. max3421e_write(MAX_REG_HXFR, (tokOUT | endpoint->address)); // Wait for completion. while (!(max3421e_read(MAX_REG_HIRQ) & bmHXFRDNIRQ)); // Clear IRQ. max3421e_write(MAX_REG_HIRQ, bmHXFRDNIRQ); rcode = (max3421e_read(MAX_REG_HRSL) & 0x0f); while (rcode && (timeout > xTaskGetTickCount()) ) { switch (rcode) { case hrNAK: nak_count++; if (nak_limit && (nak_count == USB_NAK_LIMIT)) { return (rcode); //return NAK } break; case hrTIMEOUT: retry_count++; if (retry_count == USB_RETRY_LIMIT) { return (rcode); //return TIMEOUT } break; default: return (rcode); } // Process NAK according to Host out NAK bug. max3421e_write(MAX_REG_SNDBC, 0); max3421e_write(MAX_REG_SNDFIFO, *data_p); max3421e_write(MAX_REG_SNDBC, bytes_tosend); max3421e_write(MAX_REG_HXFR, (tokOUT | endpoint->address)); //dispatch packet // Wait for the completion interrupt. while (!(max3421e_read(MAX_REG_HIRQ) & bmHXFRDNIRQ)); // Clear interrupt. max3421e_write(MAX_REG_HIRQ, bmHXFRDNIRQ); rcode = (max3421e_read(MAX_REG_HRSL) & 0x0f); } bytes_left -= bytes_tosend; data_p += bytes_tosend; } endpoint->sendToggle = (max3421e_read(MAX_REG_HRSL) & bmSNDTOGRD) ? bmSNDTOG1 : bmSNDTOG0; //update toggle // Should be 0 in all cases. return (rcode); }
/** * USB main task. Performs enumeration/cleanup */ void usb_poll(void) { uint8_t i; uint8_t rcode; uint8_t tmpdata; static uint16_t delay = 0; usb_deviceDescriptor deviceDescriptor; // Poll the MAX3421E device. max3421e_poll(); /* modify USB task state if Vbus changed */ tmpdata = max3421e_getVbusState(); switch (tmpdata) { case SE1: //illegal state usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; break; case SE0: //disconnected if ((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED) { usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; } break; case FSHOST: //attached case LSHOST: if ((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) { delay = xTaskGetTickCount() + USB_SETTLE_DELAY / portTICK_RATE_MS; usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; } break; }// switch( tmpdata switch (usb_task_state) { case USB_DETACHED_SUBSTATE_INITIALIZE: // TODO right now it looks like the USB board is just reset on disconnect. Fire disconnect for all connected // devices. for (i = 1; i < USB_NUMDEVICES; i++) if (deviceTable[i].active) usb_fireEvent(&(deviceTable[i]), USB_DISCONNECT); usb_init(); usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; break; case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here break; case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here break; case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device if (delay < xTaskGetTickCount() ) { usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; } break; case USB_ATTACHED_SUBSTATE_RESET_DEVICE: // Issue bus reset. max3421e_write(MAX_REG_HCTL, bmBUSRST); usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; break; case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: if ((max3421e_read(MAX_REG_HCTL) & bmBUSRST) == 0) { tmpdata = max3421e_read(MAX_REG_MODE) | bmSOFKAENAB; //start SOF generation max3421e_write(MAX_REG_MODE, tmpdata); // max3421e_regWr( rMODE, bmSOFKAENAB ); usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; delay = xTaskGetTickCount() + 20 / portTICK_RATE_MS; //20ms wait after reset per USB spec } break; case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order if (max3421e_read(MAX_REG_HIRQ) & bmFRAMEIRQ) { //when first SOF received we can continue if (delay < xTaskGetTickCount() ) { //20ms passed usb_task_state = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; } } break; case USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE: // toggle( BPNT_0 ); deviceTable[0].control.maxPacketSize = 8; rcode = usb_getDeviceDescriptor(&deviceTable[0], &deviceDescriptor); if (rcode == 0) { deviceTable[0].control.maxPacketSize = deviceDescriptor.bMaxPacketSize0; usb_task_state = USB_STATE_ADDRESSING; } else { usb_error = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; usb_task_state = USB_STATE_ERROR; } break; case USB_STATE_ADDRESSING: // Look for an empty spot for (i = 1; i < USB_NUMDEVICES; i++) { if (!deviceTable[i].active) { // Set correct MaxPktSize // deviceTable[i].epinfo = deviceTable[0].epinfo; deviceTable[i].address = i; deviceTable[i].active = true; usb_initEndPoint(&(deviceTable[i].control), 0); //temporary record //until plugged with real device endpoint structure rcode = usb_setAddress(&deviceTable[0], i); if (rcode == 0) { usb_fireEvent(&deviceTable[i], USB_CONNECT); // usb_task_state = USB_STATE_CONFIGURING; // NB: I've bypassed the configuring state, because configuration should be handled // in the usb event handler. usb_task_state = USB_STATE_RUNNING; } else { usb_fireEvent(&deviceTable[i], USB_ADRESSING_ERROR); // TODO remove usb_error at some point? usb_error = USB_STATE_ADDRESSING; usb_task_state = USB_STATE_ERROR; } break; //break if address assigned or error occurred during address assignment attempt } } // If no vacant spot was found in the device table, fire an error. if (usb_task_state == USB_STATE_ADDRESSING) { usb_fireEvent(&deviceTable[i], USB_ADRESSING_ERROR); // No vacant place in devtable usb_error = 0xfe; usb_task_state = USB_STATE_ERROR; } break; case USB_STATE_CONFIGURING: break; case USB_STATE_RUNNING: break; case USB_STATE_ERROR: break; } // xSerialPrintf_P(PSTR("\r\nusb_poll usb_task_state: %u @ Tick: %u"), usb_task_state, xTaskGetTickCount()); // FIXME remove this debugging }
int usb_dispatchPacket(uint8_t token, usb_endpoint * endpoint, unsigned int nakLimit) { uint32_t timeout = millis() + USB_XFER_TIMEOUT; uint8_t tmpdata; uint8_t rcode = 0; unsigned int nak_count = 0; char retry_count = 0; while (timeout > millis()) { // Analyze transfer result. // Launch the transfer. max3421e_write(MAX_REG_HXFR, (token | endpoint->address)); rcode = 0xff; // Wait for interrupt while (timeout > millis()) { tmpdata = max3421e_read(MAX_REG_HIRQ); if (tmpdata & bmHXFRDNIRQ) { // Clear the interrupt. max3421e_write(MAX_REG_HIRQ, bmHXFRDNIRQ); rcode = 0x00; break; } } // Exit if timeout. if (rcode != 0x00) return (rcode); // Wait for HRSL while (timeout > millis()) { rcode = (max3421e_read(MAX_REG_HRSL) & 0x0f); if (rcode != hrBUSY) break; // else // serialPrintf("busy!\n"); } switch (rcode) { case hrNAK: nak_count++; if (nak_count == nakLimit) return (rcode); break; case hrTIMEOUT: retry_count++; if (retry_count == USB_RETRY_LIMIT) return (rcode); break; default: return (rcode); } } return (rcode); }