Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: DMBasicGUI DMSupport
EthernetHandler.cpp
00001 #include "EthernetHandler.h" 00002 00003 #define DO_USB_IN_THIS_THREAD // If #define'd, we do our own USB comms to the GC here in this thread. 00004 // If not, we signal the main thread to do it 00005 00006 00007 #define USE_LED_FOR_DEBUGGING 00008 00009 #ifdef USE_LED_FOR_DEBUGGING 00010 00011 #include "gpio_api.h" 00012 #include "wait_api.h" 00013 #include "toolchain.h" 00014 #include "mbed_interface.h" 00015 00016 // We turn on LED 1 when we have received, from the client, a command to send to the GC. 00017 // We turn it off when we send the response back to the client. 00018 static void SetLed1(bool turnLedOn) 00019 { 00020 gpio_t led_1; gpio_init_out(&led_1, LED1); 00021 00022 if(turnLedOn) { 00023 gpio_write(&led_1, 0); // zero appears to mean "turn LED 1 on" 00024 } else { 00025 gpio_write(&led_1, 1); // one appears to turn it off 00026 } 00027 } 00028 00029 #endif // USE_LED_FOR_DEBUGGING 00030 00031 00032 #include "GuiLib.h" 00033 /* 00034 Displays the specified text string at the specified location in the currently-displayed easyGUI page. 00035 00036 Defined in main.cpp - and omits the 'ALLOW_DEBUG_PRINTS' #define 00037 */ 00038 extern void SpecialDebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y); 00039 00040 /* 00041 Tells the caller whether or not a specified GC command represents the start of a method, 00042 and that therefore a new method is now being sent to the GC. 00043 00044 Params: pointer to a null-terminated string containing the command in question 00045 00046 Returns true if the command is one that occurs at the start of a method (and nowhere else), 00047 false if not. 00048 00049 Defined in main.cpp 00050 */ 00051 bool IsStartOfMethodCommand(char *gcCommand); 00052 00053 /* 00054 Tells the caller whether or not a specified GC command represents the end of a method, 00055 and that therefore a new method has just been sent to the GC. 00056 00057 Params: pointer to a null-terminated string containing the command in question 00058 00059 Returns true if the command is one that occurs at the end of a method (and nowhere else), 00060 false if not. 00061 00062 Defined in main.cpp 00063 */ 00064 bool IsEndOfMethodCommand(char *gcCommand); 00065 00066 /* 00067 Tells the caller whether or not a specified GC command is a control command, 00068 which received "DACK" in acknowledgement, and is therefore likely 00069 to have caused the GC to change its state. 00070 00071 Params: pointer to a null-terminated string containing the command in question 00072 00073 Returns true if the command is a control command and if it succeeded, false if not. 00074 00075 Defined in main.cpp 00076 */ 00077 bool IsSuccessfulControlCommand(char *gcCommand, char *gcResponse); 00078 00079 00080 osThreadId EthernetHandler::mainThreadId = 0; 00081 bool EthernetHandler::mainThreadIdSet = false; 00082 00083 TCPSocketServer* EthernetHandler::ethernetServer = NULL; 00084 00085 USBDeviceConnected* EthernetHandler::usbDevice = NULL; 00086 USBHostGC* EthernetHandler::usbHostGC = NULL; 00087 00088 char EthernetHandler::gcCommand[20] = ""; 00089 char EthernetHandler::gcResponse[20] = ""; 00090 00091 Timer EthernetHandler::timer; 00092 00093 bool EthernetHandler::receivedGCCommand = false; 00094 00095 // This is the thread signal value we tell the main thread to use to tell us that it has received a response to the latest command. 00096 // We increment this each time we pass a new Ethernet command to the main thread, so that we know which command the response refers to. 00097 int EthernetHandler::responseReadyThreadSignalCode = GC_RESPONSE_READY; 00098 00099 // The maximum possible value for 'responseReadyThreadSignalCode' above - when it exceeds this value, we set it back to GC_RESPONSE_READY 00100 const int EthernetHandler::maxResponseReadyThreadSignalCode = GC_RESPONSE_READY + 1000; 00101 00102 00103 //#define CLIENT_SET_BLOCKING_FALSE 00104 00105 00106 bool EthernetHandler::GCCommandReceived(void) 00107 { 00108 #ifdef DO_USB_IN_THIS_THREAD 00109 return false; // We will deal with this ourselves 00110 #else 00111 return receivedGCCommand; 00112 #endif 00113 } 00114 00115 00116 /* 00117 Caller tells us the ID of the thread we are to signal 00118 when we have received a command (over the Ethernet link) 00119 to be sent to the GC. 00120 */ 00121 void EthernetHandler::SetMainThreadId(osThreadId threadId) 00122 { 00123 mainThreadId = threadId; 00124 00125 mainThreadIdSet = true; 00126 } 00127 00128 /* 00129 Caller gives us a pointer to the Ethernet server 00130 we are to use. 00131 */ 00132 void EthernetHandler::SetEthernetServer(TCPSocketServer* server) 00133 { 00134 ethernetServer = server; 00135 } 00136 00137 /* 00138 Caller gives us pointers for the USB link to the GC, 00139 to allow us to communicate with it ourselves 00140 */ 00141 void EthernetHandler::SetUsbGC(USBDeviceConnected* newUsbDevice, USBHostGC* newUsbHostGC) 00142 { 00143 usbDevice = newUsbDevice; 00144 usbHostGC = newUsbHostGC; 00145 } 00146 00147 00148 /* 00149 Passes back to the caller, the command we have received over the Ethernet link 00150 to be sent to the GC (assume the caller is going to communicate with the GC). 00151 00152 Args: pointer to a buffer into which we will copy the command 00153 pointer to an integer into which we will place the value the caller 00154 must use to tell us that it has given us the response 00155 */ 00156 void EthernetHandler::GetGCCommand(char *commandBuffer, int* responseThreadSignalCodeToUse) 00157 { 00158 strcpy(commandBuffer, gcCommand); 00159 00160 SetupNextResponseReadyThreadSignalCode(); 00161 00162 *responseThreadSignalCodeToUse = responseReadyThreadSignalCode; 00163 } 00164 00165 /* 00166 Set 'responseReadyThreadSignalCode' to the next legal value 00167 */ 00168 void EthernetHandler::SetupNextResponseReadyThreadSignalCode(void) 00169 { 00170 ++responseReadyThreadSignalCode; 00171 00172 if(responseReadyThreadSignalCode > maxResponseReadyThreadSignalCode) { 00173 responseReadyThreadSignalCode = GC_RESPONSE_READY; 00174 } 00175 } 00176 00177 00178 /* 00179 Function to allow the caller to pass to us, 00180 the response it got back from the GC. 00181 We copy it into our buffer, ready to be sent back 00182 to the Ethernet client. 00183 00184 Caller must set this *before* signaling GC_RESPONSE_READY 00185 ********************************************************* 00186 00187 Args: pointer to a buffer containing the GC response 00188 */ 00189 void EthernetHandler::SetGCResponse(char *response) 00190 { 00191 strcpy(gcResponse, response); 00192 } 00193 00194 00195 /* 00196 Sends a command (known as a 'report') to the GC, and returns the response. 00197 00198 Args: pointer to (null-terminated) command to use 00199 pointer to buffer to contain the (also null-terminated) response 00200 00201 No return code. 00202 */ 00203 void EthernetHandler::SetGCDeviceReport(char *cmd, char *response) 00204 { 00205 if((usbHostGC != NULL) && (usbDevice != NULL)) { 00206 00207 //strcpy(response, "BEFORE"); // Debug 00208 00209 // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 00210 // it is not re-entrant (and nor is the GC) 00211 while(usbHostGC->ExecutingSetDeviceReport()) { 00212 Thread::wait(1); // Give other threads a chance 00213 } 00214 00215 //strcpy(response, "AFTER 1"); // Debug 00216 00217 usbHostGC->SetDeviceReport(usbDevice, cmd, response); 00218 00219 //strcpy(response, "AFTER 2"); // Debug 00220 } 00221 } 00222 00223 00224 /* 00225 Handles the next Ethernet message from the specified client. 00226 Returns true if there was a message, false if not. 00227 */ 00228 bool EthernetHandler::HandleEthernetMessage(TCPSocketConnection* client) 00229 { 00230 if(mainThreadIdSet) { 00231 00232 receivedGCCommand = false; 00233 00234 int n = client->receive(gcCommand, sizeof(gcCommand)); 00235 if (n <= 0) return false; 00236 00237 gcCommand[n] = '\0'; 00238 00239 timer.reset(); 00240 timer.start(); 00241 00242 //#define ALLOW_DEBUG_PRINTS_HERE 00243 #ifdef ALLOW_DEBUG_PRINTS_HERE 00244 char buff[300]; 00245 sprintf(buff, "ETH - command received: \"%s\"", gcCommand); 00246 SpecialDebugPrint(buff, 50, 350); 00247 #endif // ALLOW_DEBUG_PRINTS_HERE 00248 00249 receivedGCCommand = true; 00250 00251 #ifdef USE_LED_FOR_DEBUGGING 00252 // Turn LED 1 on while we signal the main thread that we have a command, and while we wait for the main thread to respond 00253 // - or, if we are doing USB in this thread, while *we* send the command to the GC and wait for the GC to respond 00254 SetLed1(true); 00255 #endif // USE_LED_FOR_DEBUGGING 00256 00257 00258 #ifdef DO_USB_IN_THIS_THREAD 00259 00260 bool commandIdentified = false; 00261 00262 if(IsStartOfMethodCommand(gcCommand)) { 00263 commandIdentified = true; 00264 osSignalSet(mainThreadId, STARTED_DOWNLOADING_METHOD); 00265 } 00266 00267 SetGCDeviceReport(gcCommand, gcResponse); 00268 00269 if(!commandIdentified) { 00270 if(IsEndOfMethodCommand(gcCommand)) { 00271 commandIdentified = true; 00272 osSignalSet(mainThreadId, FINISHED_DOWNLOADING_METHOD); 00273 } 00274 } 00275 00276 if(!commandIdentified) { 00277 // Tell main thread if CRUN, CHON or CHOF were sent to the GC, and were successful - 00278 // i.e. acknowledged with DACK - so that it can update the display. 00279 // Also now CSTP and CABT 00280 if((gcResponse[0] == 'D') && (gcResponse[1] == 'A') && (gcResponse[2] == 'C') && (gcResponse[3] == 'K')) { /// i.e. "DACK" 00281 if(gcCommand[0] == 'C') { // Begins with 'C' - must be a command of some kind 00282 if((gcCommand[1] == 'R') && (gcCommand[2] == 'U') && (gcCommand[3] == 'N')) { // i.e. "CRUN" 00283 commandIdentified = true; 00284 osSignalSet(mainThreadId, CRUN_COMMAND_SENT); 00285 } 00286 00287 if(!commandIdentified) { 00288 if((gcCommand[1] == 'H') && (gcCommand[2] == 'O')) { 00289 if(gcCommand[3] == 'N') { // i.e. "CHON" 00290 commandIdentified = true; 00291 osSignalSet(mainThreadId, CHON_COMMAND_SENT); 00292 } 00293 00294 if(!commandIdentified) { 00295 if(gcCommand[3] == 'F') { // i.e. "CHOF" 00296 commandIdentified = true; 00297 osSignalSet(mainThreadId, CHOF_COMMAND_SENT); 00298 } 00299 } 00300 } 00301 } 00302 00303 if(!commandIdentified) { 00304 if(((gcCommand[1] == 'S') && (gcCommand[2] == 'T') && (gcCommand[3] == 'P')) // i.e. "CSTP" 00305 || ((gcCommand[1] == 'A') && (gcCommand[2] == 'B') && (gcCommand[3] == 'T'))) { // i.e. "CABT" 00306 commandIdentified = true; 00307 osSignalSet(mainThreadId, ABORT_RUN_COMMAND_SENT); 00308 } 00309 } 00310 } 00311 } 00312 } 00313 #else 00314 // Signal the main thread that we have a command to send to the GC 00315 osSignalSet(mainThreadId, GC_COMMAND_READY); 00316 00317 // Now wait for it to tell us it has 'got' the command - try and keep synchronised 00318 // (i.e. so that we do not get other commands before the main thread has dealt with this one, 00319 // and so that the commands and responses do not get out of step) 00320 Thread::signal_wait(GOT_GC_COMMAND); 00321 00322 00323 // Main thread must give us the response *now* - *before* signaling 'response ready' - see above 00324 // ********************************************************************************************* 00325 00326 // We tell the main thread to use a different response code for each command, 00327 // to try and ensure that the commands and responses do not get out of step 00328 00329 // Wait forever - do not timeout (no point in continuing without a response) 00330 Thread::signal_wait(responseReadyThreadSignalCode); 00331 00332 // Main thread has put the response in 'gcResponse' 00333 00334 receivedGCCommand = false; // Ready for the next command 00335 00336 #endif // DO_USB_IN_THIS_THREAD 00337 00338 00339 #ifdef USE_LED_FOR_DEBUGGING 00340 // We have received the command response - turn LED 1 off 00341 SetLed1(false); 00342 #endif // USE_LED_FOR_DEBUGGING 00343 00344 // Pass the response back to the client 00345 client->send_all(gcResponse, strlen(gcResponse)); 00346 00347 timer.stop(); 00348 00349 #ifdef ALLOW_DEBUG_PRINTS_HERE 00350 sprintf(buff, "ETH - response sent: \"%s\"", gcResponse); 00351 SpecialDebugPrint(buff, 50, 380); 00352 00353 if((gcResponse[1] == gcCommand[1]) && (gcResponse[2] == gcCommand[2]) && (gcResponse[3] == gcCommand[3])) { 00354 SpecialDebugPrint("ETH - command and response match", 50, 400); 00355 } else { 00356 SpecialDebugPrint("ETH - command and response *** do not match ***", 50, 400); 00357 } 00358 00359 int read_ms = timer.read_ms(); 00360 if(read_ms > 1000) { // We are interested only in suspiciously long response times (that may make Ellution time out) 00361 sprintf(buff, "ETH - time taken for %s: %d ms", gcCommand, read_ms); 00362 SpecialDebugPrint(buff, 50, 420); 00363 } 00364 #endif // ALLOW_DEBUG_PRINTS_HERE 00365 00366 //receivedGCCommand = false; // Ready for the next command 00367 00368 return true; 00369 } 00370 00371 // 'else' 00372 return false; 00373 } 00374 00375 00376 /* 00377 This is the infinite loop that handles Ethernet traffic on its own thread. 00378 Once started, it never exits. 00379 */ 00380 void EthernetHandler::HandlerFunction(void const *argument) 00381 { 00382 if(mainThreadIdSet && (ethernetServer != NULL)) { 00383 00384 // We will now sit in this loop until the end of time, or this thread gets terminated, or the system powers off, whichever happens soonest... 00385 while(true) { 00386 00387 TCPSocketConnection client; 00388 00389 // Look for a client, but do not hang if there isn't one 00390 ethernetServer->set_blocking(false); // Timeout after (1.5)s 00391 bool clientFound = (ethernetServer->accept(client) == 0); 00392 if(clientFound) { 00393 #ifdef CLIENT_SET_BLOCKING_FALSE 00394 client.set_blocking(false); // Default timeout (1.5 sec) 00395 //client.set_blocking(false, 60000); // Timeout after 1 minute (comms somehow works better than with 1.5sec) 00396 #else 00397 // No - block this thread until we get a command - what else would we do? 00398 client.set_blocking(true); 00399 // But note that, on the face of it, set_blocking(true) means that we will never close the first client connection we open - 00400 // if the client goes away, we will just sit waiting forever (in HandleEthernetMessage) for the next command(?). 00401 // In this scenario, no subsequent client will be able to establish a connection. 00402 #endif // CLIENT_SET_BLOCKING_FALSE 00403 00404 while (true) 00405 { 00406 if(!HandleEthernetMessage(&client)) break; 00407 } 00408 00409 client.close(); 00410 } 00411 } 00412 } 00413 } 00414 00415 00416 00417 00418
Generated on Tue Jul 19 2022 00:31:06 by
1.7.2