John Mitchell / lpc4088_displaymodule_GC500_2_5inch

Dependencies:   DMBasicGUI DMSupport

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers USBHostGC.cpp Source File

USBHostGC.cpp

00001 /**
00002  * A class to communicate with a USB GC.
00003  *
00004  * *** Important note - when reading data from the USB device, you must do this into memory allocated using       ***
00005  * *** the 'getSafeMem' function of the USBHost class. If you read it into 'normal' memory, your code will crash. ***
00006  * *** This is my experience so far, at least. This applies, e.g. to all the 'Get...' functions below.            ***
00007  * *** (There may, of course, be a simple explanation for this, but if so, I am not currently aware of it.)       ***
00008  * *** This is why (currently) all the 'Get...' functions allocate a buffer from safe memory,                     ***
00009  * *** read the data into it, then copy the data into the buffer provided by the caller.                          ***
00010  */
00011 
00012 #include "USBHostGC.h"
00013 
00014 extern void EasyGUIDebugPrint(char *stuffToPrint, short X, short Y);
00015 
00016 /*
00017     Displays the specified text string at the specified location in the currently-displayed easyGUI page.
00018     
00019     Defined in main.cpp - and omits the 'ALLOW_DEBUG_PRINTS' #define
00020 */
00021 extern void SpecialDebugPrint(char *stuffToPrint, short X, short Y);
00022 
00023 USBHostGC::USBHostGC()
00024 {
00025     debugWindow = NULL;
00026     
00027     usbHost = USBHost::getHostInst();
00028     
00029     reportBuffer = NULL;
00030     
00031     NoDeviceConnected(); // So far...
00032     
00033     alreadyInSetDeviceReport = false;
00034 }
00035 
00036 USBHostGC::~USBHostGC()
00037 {
00038     NoDeviceConnected();
00039 }
00040 
00041 void USBHostGC::NoDeviceConnected(void)
00042 {
00043     deviceIsGC = false;
00044     intfGC = -1;
00045     intInEndpointGC = NULL;
00046     usbGCDeviceConnected = NULL;
00047     
00048     if (reportBuffer != NULL) {
00049         usbHost->returnSafeMem(reportBuffer);
00050         reportBuffer = NULL;
00051     }
00052 }
00053 
00054 void USBHostGC::DebugPrint(const char* debugText)
00055 {
00056     if(debugWindow != NULL) {
00057         swim_put_text(debugWindow, debugText);
00058     }
00059 }
00060 
00061 /*
00062     Data sent to/from the GC over the USB link has to be in 'safe' memory - 
00063     it will crash if ordinary memory is used. This function allocates 
00064     a buffer in safe memory
00065 */
00066 void USBHostGC::AllocateReportBuffer(void)
00067 {
00068     int reportSize = GC_MESSAGE_LENGTH;
00069 
00070     if (intInEndpointGC != NULL) {
00071         int intInEndpointSize = intInEndpointGC->getSize();
00072         if (intInEndpointSize > reportSize) {
00073             reportSize = intInEndpointSize;
00074         }
00075     }
00076 
00077     reportBuffer = usbHost->getSafeMem(reportSize);
00078 }
00079 
00080 /*
00081     Attach the specified USB device to this instance
00082 */
00083 bool USBHostGC::AttachGCDevice(USBDeviceConnected* usbDeviceConnected)
00084 {
00085     intInEndpointGC = usbDeviceConnected->getEndpoint(intfGC, INTERRUPT_ENDPOINT, IN);
00086     if (!intInEndpointGC) {
00087         DebugPrint("AttachGCDevice - interrupt endpoint in not found - returning false\n");
00088         NoDeviceConnected();
00089         return false;
00090     }
00091 
00092     usbDeviceConnected->setName("GC", intfGC);
00093 
00094     usbHost->registerDriver(usbDeviceConnected, intfGC, this, &USBHostGC::NoDeviceConnected);
00095 
00096     intInEndpointGC->attach(this, &USBHostGC::RXHandler);
00097 
00098     if (reportBuffer == NULL) {
00099         AllocateReportBuffer();
00100     }
00101 
00102     usbHost->interruptRead(usbDeviceConnected, intInEndpointGC, reportBuffer, intInEndpointGC->getSize(), false);
00103 
00104     usbGCDeviceConnected = usbDeviceConnected;
00105     
00106     return true;
00107 }
00108 
00109 /*
00110     Invoked by the OS when a USB read (from the GC) has completed
00111 */
00112 void USBHostGC::RXHandler(void)
00113 {
00114     responseReceived = true;
00115     
00116     if (usbGCDeviceConnected) {
00117         usbHost->interruptRead(usbGCDeviceConnected, intInEndpointGC, reportBuffer, intInEndpointGC->getSize(), false);
00118     }
00119 }
00120 
00121 /*
00122     The SetDeviceReport function is not re-entrant (and even if it were, the GC is not, so simultaneous calls 
00123     to SetDeviceReport would not work anyway).
00124     
00125     This function tells the caller whether or not a 'SetDeviceReport' call is in progress.
00126     It returns true if so, false if not.
00127     
00128     The expectation is that the caller will wait until this function returns false
00129     before calling SetDeviceReport.
00130 */
00131 bool USBHostGC::ExecutingSetDeviceReport(void)
00132 {
00133     return alreadyInSetDeviceReport;
00134 }
00135 
00136 /*
00137     Sends a command to the GC, gets a response back. In this context, what we would think of
00138     as a 'command' is called a 'report' in the GC code - hence the use of the word 'report' here.
00139     
00140     Args: a pointer to the USB device corresponding to the GC
00141           a pointer to the command/report to be sent to the GC
00142           a pointer to the buffer to contain the GC's response
00143           
00144     Returns the USB_TYPE code giving the status of the transaction.
00145 */
00146 USB_TYPE USBHostGC::SetDeviceReport(USBDeviceConnected * dev, const char* report, char* response)
00147 {
00148     // Neither this function, nor the GC itself, are re-entrant...
00149     
00150     if(alreadyInSetDeviceReport) {
00151         return USB_TYPE_PROCESSING;
00152     }
00153     
00154     alreadyInSetDeviceReport = true;
00155     
00156     if (reportBuffer == NULL) {
00157         AllocateReportBuffer();
00158     }
00159     
00160     int i;
00161     for (i = 0; (i < GC_MESSAGE_LENGTH) && (report[i]); ++i) {
00162         reportBuffer[i] = (uint8_t) report[i];
00163     }
00164     //reportBuffer[i++] = (uint8_t) GC_CR; // Append <CR> - make *sure* it is the same code the GC expects
00165     // Fill remainder of buffer with nulls, to pad out to 10 bytes
00166     for ( ; i < GC_MESSAGE_LENGTH; ++i) {
00167         reportBuffer[i] = 0;
00168     }
00169     //reportBuffer[GC_MESSAGE_LENGTH - 1] = (uint8_t) GC_CR;
00170     
00171     responseReceived = false; // Synchronise with RXHandler
00172     
00173 //#define ALLOW_DEBUG_PRINTS_HERE
00174 #ifdef ALLOW_DEBUG_PRINTS_HERE
00175     SpecialDebugPrint("SetDeviceReport - before controlWrite", 20,80);
00176 #endif
00177     USB_TYPE t = usbHost->controlWrite( dev,
00178                          1, // Non-zero - tells GC - set report, not set configuration
00179                          SET_CONFIGURATION,
00180                          0,
00181                          0, reportBuffer, GC_MESSAGE_LENGTH);
00182     //EasyGUIDebugPrint("SetDeviceReport - after controlWrite", 20,100);
00183     
00184 #ifdef ALLOW_DEBUG_PRINTS_HERE
00185     SpecialDebugPrint("SetDeviceReport - waiting for response", 20,120);
00186 #endif    
00187 
00188     while (!responseReceived) {
00189         // wait...
00190         Thread::wait(10); // *** With this, we can successfully call this function from the Ethernet thread ***
00191                           // (without it, this function enters an infinite loop at this point when called 
00192                           // from the Ethernet thread, although it is OK from the main thread)
00193     }
00194 
00195 #ifdef ALLOW_DEBUG_PRINTS_HERE
00196     SpecialDebugPrint("SetDeviceReport - after wait for response", 20,120);
00197 #endif    
00198 
00199     // RXHandler has now received the response - return it to the caller
00200     
00201     for (i = 0; i < GC_MESSAGE_LENGTH; ++i) {
00202         response[i] = reportBuffer[i];
00203     }
00204     response[i] = '\0';  
00205     
00206     alreadyInSetDeviceReport = false;
00207     
00208     return t;
00209 }
00210 
00211 // Part of IUSBEnumerator interface
00212 /* virtual */ void USBHostGC::setVidPid(uint16_t vid, uint16_t pid)
00213 {
00214     char buff[100];
00215     sprintf(buff, "setVidPid(%d %X, %d %X)\n", vid, vid, pid, pid);
00216     DebugPrint(buff);
00217 
00218     deviceIsGC = ((vid == GC_VENDOR_ID) && ((pid == GC_PRODUCT_ID) || (pid == GC_PRODUCT_ID_2)));
00219 
00220     DebugPrint(deviceIsGC ? "Device is a GC\n" : "Device is *not* a GC\n");
00221 }
00222 
00223 // Part of IUSBEnumerator interface - must return true if the interface should be parsed
00224 /* virtual */ bool USBHostGC::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol)
00225 {
00226     char buff[100];
00227     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);
00228     DebugPrint(buff);
00229 
00230     // setVidPid should have been called before this - 
00231     // assume it will have identified whether or not the device is a GC
00232     if (deviceIsGC) {
00233         if ((intfGC == -1) &&
00234             (intf_class == HID_CLASS)) {
00235             // Don't care about subclass and protocol in this case
00236 
00237             intfGC = intf_nb;
00238             DebugPrint("  **** GC found ****  \n");
00239         }
00240         DebugPrint("GC found - parseInterface returning true\n");
00241         return true;
00242     }
00243 
00244     DebugPrint("No GC found - parseInterface returning false\n");
00245     return false;
00246 }
00247 
00248 // Part of IUSBEnumerator interface - must return true if the endpoint will be used
00249 /* virtual */ bool USBHostGC::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir)
00250 {
00251     char buff[100];
00252     sprintf(buff, "useEndPoint(%d %X, %d %X, %d %X)\n", intf_nb, intf_nb, type, type, dir, dir);
00253     DebugPrint(buff);
00254 
00255     if (deviceIsGC) {
00256         if ((intfGC == intf_nb) &&
00257             (type == INTERRUPT_ENDPOINT) &&
00258             (dir == IN)) {
00259             DebugPrint("GC endpoint found - useEndPoint returning true\n");
00260             return true;
00261         }
00262     }
00263 
00264     // 'else'
00265     DebugPrint("GC endpoint not found - useEndpoint returning false\n");
00266     return false;
00267 }