diff --git a/src/gnss.c b/src/gnss.c index d2ab48e..b538942 100644 --- a/src/gnss.c +++ b/src/gnss.c @@ -8,8 +8,13 @@ #include "manager.h" #include "at.h" +#include +#include +#include + #define BUFFER_SIZE 256 -#define UPLOAD_DELAY 100000 +#define UPLOAD_DELAY_US 10000 +#define UPLOAD_TIMEOUT_S 10 #define RESCHEDULE_IN_SECS 30 static void gnss_step(struct EG25Manager *manager); @@ -195,71 +200,71 @@ static void state_at_gnss(struct EG25Manager *manager) static void fetch_assistance_data(struct EG25Manager *manager) { - CURL *curl; CURLcode response; - long status_code; - gchar *url = NULL; + curl_off_t downloaded; + CURL *curl = NULL; + g_autofree gchar *url = NULL; FILE *tmp_file = NULL; - long int size; + gchar errbuf[CURL_ERROR_SIZE]; + errbuf[0] = 0; /* Fetch assistance data with curl */ tmp_file = fdopen(manager->gnss_assistance_fd, "wb+"); + if (tmp_file == NULL) { + g_critical("Unable to open file to save assistance data: %s", + g_strerror(errno)); + goto bail; + } + lseek(manager->gnss_assistance_fd, 0, SEEK_SET); ftruncate(manager->gnss_assistance_fd, 0); url = g_strconcat(manager->gnss_assistance_url, "/", manager->gnss_assistance_file, NULL); + curl = curl_easy_init(); - if (!curl) - g_error ("Unable to initialize curl"); + if (!curl) { + g_critical("Unable to initialize curl"); + goto bail; + } + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, tmp_file); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); - response = curl_easy_perform(curl); - if (response == CURLE_HTTP_RETURNED_ERROR) { - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); - curl_easy_cleanup(curl); - g_warning ("Unable to fetch GNSS assistance data from %s (HTTP %ld)", - url, status_code); - /* Restart upload on HTTP error status code */ - g_message ("Rescheduling upload because of failure in %ds", - RESCHEDULE_IN_SECS); - manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; - g_timeout_add_seconds(RESCHEDULE_IN_SECS, - G_SOURCE_FUNC(gnss_upload_assistance_data), - manager); - return; + response = curl_easy_perform(curl); + if (response != CURLE_OK) { + g_warning("Unable to fetch GNSS assistance data from %s: %s", + url, strlen(errbuf) ? errbuf : curl_easy_strerror(response)); + goto bail; } - /* Get file size in bytes */ - size = (long int)lseek(manager->gnss_assistance_fd, 0, SEEK_END); - lseek(manager->gnss_assistance_fd, 0, SEEK_SET); - - if (size <= 0) { - g_warning ("GNSS assistance data contains 0 bytes," - "check network connection."); - /* - * Restart upload when file does not contain any data, - * mostly because of no network connection. - */ - g_message ("Rescheduling upload because of failure in %ds", - RESCHEDULE_IN_SECS); - manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; - g_timeout_add_seconds(RESCHEDULE_IN_SECS, - G_SOURCE_FUNC(gnss_upload_assistance_data), - manager); - return; + response = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &downloaded); + if (response) { + g_critical("Unable to get number of downloaded bytes from curl"); + goto bail; + } else if (downloaded <= 0) { + g_warning("Downloaded empty assistance data file"); + goto bail; } g_message("Fetching GNSS assistance data from %s was successfull", url); + + fflush(tmp_file); curl_easy_cleanup(curl); g_free(url); /* Go to the next step */ manager->gnss_assistance_step++; gnss_step(manager); + return; + +bail: + if (curl != NULL) + curl_easy_cleanup(curl); + manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; } /******************************************************************************/ @@ -279,18 +284,24 @@ static void init_assistance_data_upload_start(struct EG25Manager *manager, const char *response) { gchar value[BUFFER_SIZE]; - long int size; + off_t size; /* Process AT response */ at_process_result(manager, response); /* Get file size in bytes */ - size = (long int)lseek(manager->gnss_assistance_fd, 0, SEEK_END); + size = lseek(manager->gnss_assistance_fd, 0, SEEK_END); + if (size == -1) { + g_critical("gnss: unable to read size of xtra data file: %s", g_strerror(errno)); + + manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; + return; + } lseek(manager->gnss_assistance_fd, 0, SEEK_SET); /* Start upload */ - g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\",%ld\r\n", - manager->gnss_assistance_file, size); + g_snprintf(value, BUFFER_SIZE, "\"RAM:%s\",%ld,%d", + manager->gnss_assistance_file, size, UPLOAD_TIMEOUT_S); g_message("Initiate GNSS assistance data upload: %s", value); at_append_command(manager, "QFUPL", NULL, value, NULL, init_assistance_data_upload_ready); @@ -310,39 +321,39 @@ static void init_assistance_data_upload(struct EG25Manager *manager) static void upload_assistance_data(struct EG25Manager *manager) { - char buffer[2*BUFFER_SIZE]; - gint len; - gboolean success = TRUE; + gint error; + glong written_total = 0; + gint ret; + struct stat sb; - /* Copy downloaded XTRA assistance data to the modem over serial */ - while((len = read(manager->gnss_assistance_fd, buffer, 2*BUFFER_SIZE)) > 0) - { - len = write(manager->at_fd, buffer, len); - if (len < 0) { - success = FALSE; - g_error("Writing GNSS assistance data failed: %d", len); - break; - } - usleep(UPLOAD_DELAY); - g_message("Uploaded %d bytes", len); + if (fstat(manager->gnss_assistance_fd, &sb) != 0) { + g_critical("gnss: unable to stat xtra data file: %s", g_strerror(errno)); + + /* Make sure the upload times out and the modem goes back to AT command mode */ + sleep(UPLOAD_TIMEOUT_S + 1); + manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; + return; } + do { + errno = 0; + /* Copy downloaded XTRA assistance data to the modem over serial */ + ret = sendfile(manager->at_fd, manager->gnss_assistance_fd, &written_total, sb.st_size); + error = errno; + usleep(UPLOAD_DELAY_US); + } while ((!error && written_total < sb.st_size) || (ret == -1 && error == EAGAIN)); + /* Clear QFUPL AT command and process next */ at_next_command(manager); /* Go to the next step if successful */ - if (success) { + if (!error) { + g_message("Successfully uploaded %ld bytes to the modem", written_total); manager->gnss_assistance_step++; gnss_step(manager); - } - /* Restart upload */ - else { - g_message ("Rescheduling upload because of failure in %ds", - RESCHEDULE_IN_SECS); + } else { + g_critical("Unable to upload xtra data: %s", g_strerror(error)); manager->gnss_assistance_step = EG25_GNSS_STEP_LAST; - g_timeout_add_seconds(RESCHEDULE_IN_SECS, - G_SOURCE_FUNC(gnss_upload_assistance_data), - manager); } } @@ -385,9 +396,9 @@ static void finish_assistance_data_upload(struct EG25Manager *manager) #ifdef HAVE_MMGLIB static void enable_mm_gnss(struct EG25Manager *manager) { - MMModemLocationSource sources; - gboolean signal_location; g_autoptr (GError) error = NULL; + MMModemLocationSource sources = mm_modem_location_get_enabled(manager->mm_location); + gboolean signal_location = mm_modem_location_signals_location(manager->mm_location); if (manager->gnss_sources & EG25_GNSS_SOURCE_UNMANAGED) sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; @@ -396,8 +407,6 @@ static void enable_mm_gnss(struct EG25Manager *manager) if (manager->gnss_sources & EG25_GNSS_SOURCE_RAW) sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; - sources = mm_modem_location_get_enabled(manager->mm_location); - signal_location = mm_modem_location_signals_location(manager->mm_location); mm_modem_location_setup_sync(manager->mm_location, sources, signal_location, NULL, &error); if (error != NULL)