// Raspberry Pi LPC1114 I/O Processor Expansion Board // SPI Agent firmware services over HTTP // Copyright (C)2013-2018, Philip Munts, President, Munts AM Corp. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include #define SERVICENAME "spiagent-http" #define REGEX_RESPONSE_TEMPLATE "^[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+;$" static regex_t response_template; static char URL[256]; static CURL *curlhandle; static char curlerrormsg[CURL_ERROR_SIZE]; static char curlresponse[256]; static size_t min(size_t x, size_t y) { if (x > y) return y; else return x; } static void strrtrim(char *s) { int n = strlen(s); while ((n > 0) && isspace(s[n-1])) { s[n-1] = 0; n--; } } static size_t curlcallback(char *ptr, size_t size, size_t nmemb, void *userdata) { size_t count = min(sizeof(curlresponse) - 1, size*nmemb); memset(curlresponse, 0, sizeof(curlresponse)); memcpy(curlresponse, ptr, count); return size*nmemb; } void spiagent_command_http(SPIAGENT_COMMAND_MSG_t *cmd, SPIAGENT_RESPONSE_MSG_t *resp, int32_t *error) { char query[1024]; int servererror; // Check whether we have an open connection if (curlhandle == NULL) { *error = EBADF; return; } // Build the query string snprintf(query, sizeof(query), "%s?%d,%d,%d", URL, cmd->command, cmd->pin, cmd->data); // Perform the HTTP transfer if (curl_easy_setopt(curlhandle, CURLOPT_URL, query)) { #ifdef DEBUG fprintf(stderr, "ERROR: curl_easy_setopt() for CURLOPT_URL failed\n"); #endif *error = EIO; return; } if (curl_easy_perform(curlhandle)) { #ifdef DEBUG fprintf(stderr, "ERROR: curl_easy_perform() failed, %s\n", curlerrormsg); #endif *error = EIO; return; } // Sanitize the response curlresponse[sizeof(curlresponse)-1] = 0; // Guarantee NUL terminator strrtrim(curlresponse); // Strip trailing whitespace // The query string must be of the form n,n,n,n,n if (regexec(&response_template, curlresponse, 0, NULL, 0)) { #ifdef DEBUG fprintf(stderr, "ERROR: regexec() failed, invalid response\n"); #endif *error = EIO; return; } // Decode the query string if (sscanf(curlresponse, "%d,%u,%u,%u,%u;", &servererror, &resp->command, &resp->pin, &resp->data, &resp->error) != 5) { #ifdef DEBUG fprintf(stderr, "ERROR: sscanf() failed, invalid response\n"); #endif *error = EIO; return; } // Check for server error if (servererror) { #ifdef DEBUG fprintf(stderr, "ERROR: server returned error %d\n", servererror); #endif *error = EIO; return; } *error = 0; } void spiagent_open_http(const char *servername, int32_t *error) { struct servent *service; SPIAGENT_COMMAND_MSG_t cmd; SPIAGENT_RESPONSE_MSG_t resp; // Check whether we have an open connection if (curlhandle != NULL) { *error = EBUSY; return; } // NULL servername is not allowed if (servername == NULL) { #ifdef DEBUG fprintf(stderr, "ERROR: server name is NULL\n"); #endif *error = EINVAL; return; } // Look up the service service = getservbyname(SERVICENAME, "tcp"); if (service == NULL) { #ifdef DEBUG fprintf(stderr, "ERROR: getservbyname() failed, unknown service\n"); #endif *error = EIO; return; } // Compile the regular expression template if (regcomp(&response_template, REGEX_RESPONSE_TEMPLATE, REG_EXTENDED)) { #ifdef DEBUG fprintf(stderr, "ERROR: regcomp() failed\n"); #endif *error = EIO; return; } // Build the HTTP server URL memset(URL, 0, sizeof(URL)); snprintf(URL, sizeof(URL), "http://%s:%d/SPIAGENT", servername, ntohs(service->s_port)); // Initialize the curl library curlhandle = curl_easy_init(); if (curlhandle == NULL) { #ifdef DEBUG fprintf(stderr, "ERROR: curl_easy_init() failed\n"); #endif regfree(&response_template); *error = EIO; return; } if (curl_easy_setopt(curlhandle, CURLOPT_ERRORBUFFER, curlerrormsg)) { #ifdef DEBUG fprintf(stderr, "ERROR: curl_easy_setopt() for CURLOPT_ERRORBUFFER failed\n"); #endif regfree(&response_template); curl_easy_cleanup(curlhandle); curlhandle = NULL; *error = EIO; return; } if (curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, curlcallback)) { #ifdef DEBUG fprintf(stderr, "ERROR: curl_easy_setopt() for CURLOPT_WRITEFUNCTION failed\n"); #endif regfree(&response_template); curl_easy_cleanup(curlhandle); curlhandle = NULL; *error = EIO; return; } // Ping the server memset(&cmd, 0, sizeof(cmd)); cmd.command = SPIAGENT_CMD_NOP; memset(&resp, 0, sizeof(resp)); spiagent_command_http(&cmd, &resp, error); if (*error) { #ifdef DEBUG fprintf(stderr, "ERROR: spiagent_command_http() failed, error=%d\n", *error); #endif regfree(&response_template); curl_easy_cleanup(curlhandle); curlhandle = NULL; *error = EIO; return; } #ifdef DEBUG fprintf(stderr, "DEBUG: SPI Agent Firmware version is %d\n", resp.data); #endif *error = 0; } void spiagent_close_http(int32_t *error) { // Check whether we have an open connection if (curlhandle == NULL) { *error = EBADF; return; } regfree(&response_template); curl_easy_cleanup(curlhandle); curlhandle = NULL; *error = 0; }