Repository for import to local machine
Dependencies: DMBasicGUI DMSupport
USBHostGC.cpp
- Committer:
- jmitc91516
- Date:
- 2017-07-20
- Revision:
- 1:a5258871b33d
- Parent:
- 0:47c880c1463d
File content as of revision 1:a5258871b33d:
/** * A class to communicate with a USB GC. * * *** Important note - when reading data from the USB device, you must do this into memory allocated using *** * *** the 'getSafeMem' function of the USBHost class. If you read it into 'normal' memory, your code will crash. *** * *** This is my experience so far, at least. This applies, e.g. to all the 'Get...' functions below. *** * *** (There may, of course, be a simple explanation for this, but if so, I am not currently aware of it.) *** * *** This is why (currently) all the 'Get...' functions allocate a buffer from safe memory, *** * *** read the data into it, then copy the data into the buffer provided by the caller. *** */ #include "USBHostGC.h" extern void EasyGUIDebugPrint(char *stuffToPrint, short X, short Y); /* Displays the specified text string at the specified location in the currently-displayed easyGUI page. Defined in main.cpp - and omits the 'ALLOW_DEBUG_PRINTS' #define */ extern void SpecialDebugPrint(char *stuffToPrint, short X, short Y); USBHostGC::USBHostGC() { debugWindow = NULL; usbHost = USBHost::getHostInst(); reportBuffer = NULL; NoDeviceConnected(); // So far... alreadyInSetDeviceReport = false; } USBHostGC::~USBHostGC() { NoDeviceConnected(); } void USBHostGC::NoDeviceConnected(void) { deviceIsGC = false; intfGC = -1; intInEndpointGC = NULL; usbGCDeviceConnected = NULL; if (reportBuffer != NULL) { usbHost->returnSafeMem(reportBuffer); reportBuffer = NULL; } } void USBHostGC::DebugPrint(const char* debugText) { if(debugWindow != NULL) { swim_put_text(debugWindow, debugText); } } /* Data sent to/from the GC over the USB link has to be in 'safe' memory - it will crash if ordinary memory is used. This function allocates a buffer in safe memory */ void USBHostGC::AllocateReportBuffer(void) { int reportSize = GC_MESSAGE_LENGTH; if (intInEndpointGC != NULL) { int intInEndpointSize = intInEndpointGC->getSize(); if (intInEndpointSize > reportSize) { reportSize = intInEndpointSize; } } reportBuffer = usbHost->getSafeMem(reportSize); } /* Attach the specified USB device to this instance */ bool USBHostGC::AttachGCDevice(USBDeviceConnected* usbDeviceConnected) { intInEndpointGC = usbDeviceConnected->getEndpoint(intfGC, INTERRUPT_ENDPOINT, IN); if (!intInEndpointGC) { DebugPrint("AttachGCDevice - interrupt endpoint in not found - returning false\n"); NoDeviceConnected(); return false; } usbDeviceConnected->setName("GC", intfGC); usbHost->registerDriver(usbDeviceConnected, intfGC, this, &USBHostGC::NoDeviceConnected); intInEndpointGC->attach(this, &USBHostGC::RXHandler); if (reportBuffer == NULL) { AllocateReportBuffer(); } usbHost->interruptRead(usbDeviceConnected, intInEndpointGC, reportBuffer, intInEndpointGC->getSize(), false); usbGCDeviceConnected = usbDeviceConnected; return true; } /* Invoked by the OS when a USB read (from the GC) has completed */ void USBHostGC::RXHandler(void) { responseReceived = true; if (usbGCDeviceConnected) { usbHost->interruptRead(usbGCDeviceConnected, intInEndpointGC, reportBuffer, intInEndpointGC->getSize(), false); } } /* The SetDeviceReport function is not re-entrant (and even if it were, the GC is not, so simultaneous calls to SetDeviceReport would not work anyway). This function tells the caller whether or not a 'SetDeviceReport' call is in progress. It returns true if so, false if not. The expectation is that the caller will wait until this function returns false before calling SetDeviceReport. */ bool USBHostGC::ExecutingSetDeviceReport(void) { return alreadyInSetDeviceReport; } /* Sends a command to the GC, gets a response back. In this context, what we would think of as a 'command' is called a 'report' in the GC code - hence the use of the word 'report' here. Args: a pointer to the USB device corresponding to the GC a pointer to the command/report to be sent to the GC a pointer to the buffer to contain the GC's response Returns the USB_TYPE code giving the status of the transaction. */ USB_TYPE USBHostGC::SetDeviceReport(USBDeviceConnected * dev, const char* report, char* response) { // Neither this function, nor the GC itself, are re-entrant... if(alreadyInSetDeviceReport) { return USB_TYPE_PROCESSING; } alreadyInSetDeviceReport = true; if (reportBuffer == NULL) { AllocateReportBuffer(); } int i; for (i = 0; (i < GC_MESSAGE_LENGTH) && (report[i]); ++i) { reportBuffer[i] = (uint8_t) report[i]; } //reportBuffer[i++] = (uint8_t) GC_CR; // Append <CR> - make *sure* it is the same code the GC expects // Fill remainder of buffer with nulls, to pad out to 10 bytes for ( ; i < GC_MESSAGE_LENGTH; ++i) { reportBuffer[i] = 0; } //reportBuffer[GC_MESSAGE_LENGTH - 1] = (uint8_t) GC_CR; responseReceived = false; // Synchronise with RXHandler //#define ALLOW_DEBUG_PRINTS_HERE #ifdef ALLOW_DEBUG_PRINTS_HERE SpecialDebugPrint("SetDeviceReport - before controlWrite", 20,80); #endif USB_TYPE t = usbHost->controlWrite( dev, 1, // Non-zero - tells GC - set report, not set configuration SET_CONFIGURATION, 0, 0, reportBuffer, GC_MESSAGE_LENGTH); //EasyGUIDebugPrint("SetDeviceReport - after controlWrite", 20,100); #ifdef ALLOW_DEBUG_PRINTS_HERE SpecialDebugPrint("SetDeviceReport - waiting for response", 20,120); #endif while (!responseReceived) { // wait... Thread::wait(10); // *** With this, we can successfully call this function from the Ethernet thread *** // (without it, this function enters an infinite loop at this point when called // from the Ethernet thread, although it is OK from the main thread) } #ifdef ALLOW_DEBUG_PRINTS_HERE SpecialDebugPrint("SetDeviceReport - after wait for response", 20,120); #endif // RXHandler has now received the response - return it to the caller for (i = 0; i < GC_MESSAGE_LENGTH; ++i) { response[i] = reportBuffer[i]; } response[i] = '\0'; alreadyInSetDeviceReport = false; return t; } // Part of IUSBEnumerator interface /* virtual */ void USBHostGC::setVidPid(uint16_t vid, uint16_t pid) { char buff[100]; sprintf(buff, "setVidPid(%d %X, %d %X)\n", vid, vid, pid, pid); DebugPrint(buff); deviceIsGC = ((vid == GC_VENDOR_ID) && ((pid == GC_PRODUCT_ID) || (pid == GC_PRODUCT_ID_2))); DebugPrint(deviceIsGC ? "Device is a GC\n" : "Device is *not* a GC\n"); } // Part of IUSBEnumerator interface - must return true if the interface should be parsed /* virtual */ bool USBHostGC::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) { char buff[100]; sprintf(buff, "parseInterface(%d %X, %d %X, %d %X, %d %X)\n", intf_nb, intf_nb, intf_class, intf_class, intf_subclass, intf_subclass, intf_protocol, intf_protocol); DebugPrint(buff); // setVidPid should have been called before this - // assume it will have identified whether or not the device is a GC if (deviceIsGC) { if ((intfGC == -1) && (intf_class == HID_CLASS)) { // Don't care about subclass and protocol in this case intfGC = intf_nb; DebugPrint(" **** GC found **** \n"); } DebugPrint("GC found - parseInterface returning true\n"); return true; } DebugPrint("No GC found - parseInterface returning false\n"); return false; } // Part of IUSBEnumerator interface - must return true if the endpoint will be used /* virtual */ bool USBHostGC::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) { char buff[100]; sprintf(buff, "useEndPoint(%d %X, %d %X, %d %X)\n", intf_nb, intf_nb, type, type, dir, dir); DebugPrint(buff); if (deviceIsGC) { if ((intfGC == intf_nb) && (type == INTERRUPT_ENDPOINT) && (dir == IN)) { DebugPrint("GC endpoint found - useEndPoint returning true\n"); return true; } } // 'else' DebugPrint("GC endpoint not found - useEndpoint returning false\n"); return false; }