]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lib.git/blobdiff - rpp/src/rpp/sdr.c
Change license to MIT
[pes-rpp/rpp-lib.git] / rpp / src / rpp / sdr.c
index f328a9ca1a0d025fb362e77adc03c7fbea75848e..8dd8ce7aad27bdb460f2110118069ff5fc974197 100644 (file)
@@ -1,24 +1,32 @@
-/* Copyright (C) 2013 Czech Technical University in Prague
+/* Copyright (C) 2013, 2014, 2015 Czech Technical University in Prague
  *
  * Authors:
  *     - Carlos Jenkins <carlos@jenkins.co.cr>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  *
  * File : sdr.c
  * Abstract:
- *     SD-RAN logging RPP API implementation file.
+ *     SD-RAM logging RPP API implementation file.
  *
  * References:
  *     sdr.h
 
 
 #include "rpp/rpp.h"
+#include <stdio.h>  // vsnprintf()
+#include <ctype.h>  // isprint()
+#include <string.h> // strncmp()
+#include <stdarg.h> // va_start, va_end
 
-#if rppCONFIG_INCLUDE_SDR == 1
+#ifndef FREERTOS_POSIX
+#include "drv/drv.h"
+#define echo(x) rpp_sci_putc(x)
+#else
+#define echo(x) (void)(x)
+#endif
 
+static const char *prompt  = "--> ";
+static const char *newline = "\r\n";
+
+// extern this semaphore to wait for cmdproc to exit
+xSemaphoreHandle rpp_sdr_cmdproc_semaphore;
+
+
+/// Memory management variables ------------------------------------------------
+static xSemaphoreHandle memory_mutex;
+static uint32_t memory_size    = 0;
+static uint8_t *memory_start   = NULL;
+static uint8_t *memory_end     = NULL;
+static uint8_t *memory_current = NULL;
+
+
+
+/// Tasks control --------------------------------------------------------------
+// Task handle for command processor task
+static xTaskHandle cmdproc_handle;
+
+// Task handle for log show task
+static xTaskHandle show_handle;
+
+// Flag the request the tasks to stop
+static boolean_t stop_tasks = FALSE;
+
+// Number of tasks running
+static uint8_t tasks_running = 0;
+
+// Force context change to other tasks until all
+// the tasks created by this module are deleted.
+static void wait_tasks_to_finish()
+{
+       stop_tasks = TRUE;
+       while (tasks_running > 0)
+               taskYIELD();
+       stop_tasks = FALSE;
+}
+
+
+
+/// Show log task --------------------------------------------------------------
+// Semaphore to order the show task to start flushing the log
+static xSemaphoreHandle show_semaphore;
+
+// Flag to check if log show task is flushing the log
+static boolean_t show_flushing = FALSE;
+
+// Show log task
+void rpp_sdr_showtask(void *p)
+{
+       uint8_t *current;
+       uint8_t byte;
+
+       while (!stop_tasks) {
+
+               // Wait semaphore to start
+               if (xSemaphoreTake(show_semaphore, 1) != pdTRUE)
+                       // This will wake up each tick to check if
+                       // deletion of this task was requested.
+                       continue;
+               current = memory_start;
+
+               // Iterate until the end of the log
+               while (show_flushing &&
+                          (current != memory_current) &&
+                          (current < memory_end)) { // Just in case
+
+                       // Print characters in this memory location.
+                       // Ignores non-printable characters except \r and \n
+                       byte = *current;
+                       if ((byte == '\r') || (byte == '\n') || isprint(byte))
+                               rpp_sci_putc(byte);
+                       current++;
+               }
+
+               // If user waited to finish
+               if (show_flushing) {
+                       rpp_sci_printf((const char *)"%s", newline);
+                       rpp_sci_printf((const char *)"%s", prompt);
+                       show_flushing = FALSE;
+               }
+       }
+
+       // Delete myself
+       tasks_running--;
+       vTaskDelete(NULL);
+}
+
+
+
+/// Command processor task -----------------------------------------------------
+#define BUF_SIZE 80
+
+// Buffer to store incomming command
+static char in_buffer[BUF_SIZE];
+
+// SCI log command processor task
+void rpp_sdr_cmdproc(void *p)
+{
+       rpp_sci_printf((const char *)
+                                  "Log control: %dkB available.\r\n",
+                                  (rpp_sdr_available() / 1024)
+                                  );
+       rpp_sci_printf((const char *)
+                                  "===========================================================\r\n"
+                                  );
+       rpp_sci_printf((const char *)"%s", prompt);
+
+       uint8_t input = 0;
+       uint8_t buff_index = 0;
+       boolean_t flush = FALSE;
+       while (!stop_tasks) {
+
+               // Get one character from the user
+               if (rpp_sci_read_nb(1, &input) != SUCCESS) {
+                       if (!stop_tasks)
+                               vTaskDelay(50 / portTICK_RATE_MS);
+                       continue;
+               }
+
+               // Stop flushing if one character is received
+               if (show_flushing) {
+                       rpp_sdr_show(FALSE);
+                       vTaskDelay(100 / portTICK_RATE_MS);
+                       rpp_sci_printf("%s", newline);
+                       rpp_sci_printf("%s", prompt);
+                       continue;
+               }
+
+               // Backspace and Delete
+               if (input == 8 || input == 127) {
+                       if (buff_index > 0) {
+                               buff_index--;
+                               echo('\b');
+                               echo(' ' );
+                               echo('\b');
+                       }
+
+                       // Line feed or Carriage return
+               }
+               else if (input == 10 || input == 13) {
+                       flush = TRUE;
+                       echo('\r');
+                       echo('\n');
+
+                       // If is any printable character
+               }
+               else if (isprint(input)) {
+
+                       // Store character and increment buffer index
+                       in_buffer[buff_index] = input;
+                       buff_index++;
+                       echo(input);
+
+                       // Check if buffer is full and force flush
+                       if (buff_index == BUF_SIZE - 1)
+                               flush = TRUE;
+               }
+               // All other character are ignored
+
+               // Flush buffer
+               if (flush) {
+
+                       // Terminate string
+                       in_buffer[buff_index] = '\0';
+
+
+                       // Re-prompt
+                       if (buff_index == 0) {
+                               rpp_sci_printf((const char *)"%s", newline);
+                               rpp_sci_printf((const char *)"%s", prompt);
+
+                       }
+                       // Help command
+                       else if (strncmp(in_buffer, "help", BUF_SIZE) == 0) {
+
+                               rpp_sci_printf((const char *)
+                                                          "Available commands:\r\n"
+                                                          );
+                               rpp_sci_printf((const char *)
+                                                          "\tlog       - Show the log.\r\n"
+                                                          );
+                               rpp_sci_printf((const char *)
+                                                          "\tclear     - Clear the log.\r\n"
+                                                          );
+                               rpp_sci_printf((const char *)
+                                                          "\tavailable - Display amount of memory left.\r\n"
+                                                          );
+                               rpp_sci_printf((const char *)
+                                                          "\texit      - Exit this command processor.\r\n"
+                                                          );
+
+                               rpp_sci_printf((const char *)"%s", newline);
+                               rpp_sci_printf((const char *)"%s", prompt);
+                       }
+                       // Log command
+                       else if (strncmp(in_buffer, "log", BUF_SIZE) == 0)
+                               rpp_sdr_show(TRUE);
+
+                       // Clear command
+                       else if (strncmp(in_buffer, "clear", BUF_SIZE) == 0) {
+                               rpp_sdr_clear();
+                               rpp_sci_printf((const char *)"Done.\r\n");
+                               rpp_sci_printf((const char *)"%s", newline);
+                               rpp_sci_printf((const char *)"%s", prompt);
+
+                       }
+                       // Available command
+                       else if (strncmp(in_buffer, "available", BUF_SIZE) == 0) {
+                               rpp_sci_printf(
+                                       (const char *)"%d kB of %d kB available.\r\n",
+                                       rpp_sdr_available() / 1024,
+                                       memory_size / 1024
+                                       );
+                               rpp_sci_printf((const char *)"%s", newline);
+                               rpp_sci_printf((const char *)"%s", prompt);
+
+                       }
+                       // Exit command
+                       else if (strncmp(in_buffer, "exit", BUF_SIZE) == 0) {
+                               xSemaphoreGive(rpp_sdr_cmdproc_semaphore);
+                               tasks_running--;
+                               vTaskDelete(NULL);
+
+                       }
+                       // Unknown command, print buffer back
+                       else {
+                               rpp_sci_printf(
+                                       (const char *)"ERROR: Unknown command \"%s\"\r\n",
+                                       (char *)&in_buffer);
+                               rpp_sci_printf((const char *)"%s", prompt);
+                       }
+
+                       // Reset variables
+                       rpp_sci_flush(TRUE);
+                       buff_index = 0;
+                       flush = FALSE;
+               }
+       }
+
+       // Delete myself
+       tasks_running--;
+       vTaskDelete(NULL);
+}
+
+
+
+/// Public API -----------------------------------------------------------------
+// Flag to check if SDR module is initialized
 static boolean_t initialized = FALSE;
 
+// Memory for Simulation only
+#ifdef FREERTOS_POSIX
+static uint8_t memory_simulation[1024*1024]; // Allocate 1MB for test
+#endif
+
+// Initialize SDR module
 int8_t rpp_sdr_init()
 {
-    if(initialized) {
-        return FAIL;
-    }
-    initialized = TRUE;
+       if (initialized)
+               return FAILURE;
+       initialized = TRUE;
+
+       // Create memory write mutex
+       memory_mutex = xSemaphoreCreateMutex();
 
-    // FIXME: Implement.
-   return SUCCESS;
+       // Create log show semaphore
+       vSemaphoreCreateBinary(show_semaphore);
+       xSemaphoreTake(show_semaphore, 0);
+
+       // Create semaphore for outer applications to wait cmdproc to exit.
+       // Non static! The symbol should be exported, so use the full prefix.
+       vSemaphoreCreateBinary(rpp_sdr_cmdproc_semaphore);
+       xSemaphoreTake(rpp_sdr_cmdproc_semaphore, 0);
+
+       // Define memory bounds
+#ifndef FREERTOS_POSIX
+       memory_size    = RPP_SDR_ADDR_END - RPP_SDR_ADDR_START + 1;
+       memory_start   = (uint8_t *)RPP_SDR_ADDR_START;
+       memory_end     = (uint8_t *)RPP_SDR_ADDR_END;
+#else
+       memory_size    = sizeof(memory_simulation);
+       memory_start   = (uint8_t *)&memory_simulation;
+       memory_end     = (uint8_t *)(memory_start + memory_size - 1);
+#endif
+       memory_current = memory_start;
+
+       // Low level init
+#ifndef FREERTOS_POSIX
+       emif_SDRAMInit();
+#endif
+
+       return SUCCESS;
+}
+
+
+// General flag to check if logging is enabled
+static boolean_t log_enabled = FALSE;
+
+// Enable/Disable logging
+int8_t rpp_sdr_setup(boolean_t enable)
+{
+       // Just in case user ignore everything
+       if (!initialized)
+               return FAILURE;
+
+       // No change, ignore
+       if (log_enabled == enable)
+               return FAILURE;
+
+       // Shut down is requested
+       if (log_enabled && !enable) {
+               // Stop show task if running
+               rpp_sdr_show(FALSE);
+               // Delete tasks
+               wait_tasks_to_finish();
+               // Disable logging
+               log_enabled = FALSE;
+
+               // Startup is requested
+       }
+       else {
+               if (xTaskCreate(rpp_sdr_showtask,
+                                               "rpp_sdr_showtask",
+                                               256, NULL, 2, &show_handle) != pdPASS)
+                       return FAILURE;
+               tasks_running++;
+
+               if (xTaskCreate(rpp_sdr_cmdproc,
+                                               "rpp_sdr_cmdproc",
+                                               512, NULL, 2, &cmdproc_handle) != pdPASS) {
+                       wait_tasks_to_finish();
+                       return FAILURE;
+               }
+               tasks_running++;
+
+               log_enabled = TRUE;
+       }
+
+       return SUCCESS;
 }
 
 
-#endif /* rppCONFIG_INCLUDE_SDR */
+// Memory available
+uint32_t rpp_sdr_available()
+{
+       return (uint32_t)(memory_end - memory_current + 1);
+}
+
+
+// Store something to the log, if logging is enabled
+int32_t rpp_sdr_printf(const char *format, ...)
+{
+       if (!log_enabled)
+               return FAILURE;
+
+       // Don't even try if memory is full
+       if (memory_current == memory_end)
+               return FAILURE;
+
+       /// Format user string
+       char str[MAX_BUFFER_LEN];
+       int length = -1;
+
+       va_list argList;
+       va_start(argList, format);
+
+       length = vsnprintf(str, sizeof(str), format, argList);
+
+       va_end(argList);
+
+       if (length < 1)
+               return length;
+
+
+       /// Format header
+       // uint32_t max value is 4294967295 (10 digits) + [] + ' ' + '\0' = 14
+       char hdr[14];
+       int hdr_length = -1;
+       hdr_length = sprintf(hdr, (const char *)"[%10d] ", xTaskGetTickCount());
+
+       if (hdr_length < 1)
+               return hdr_length;
 
+
+       /// Write header
+       uint32_t cnt = 0;
+       int i = 0;
+       xSemaphoreTake(memory_mutex, portMAX_DELAY);
+       while ((memory_current != memory_end) && (i < hdr_length)) {
+
+               *memory_current = hdr[i];
+
+               memory_current++;
+               cnt++;
+               i++;
+       }
+
+
+       /// Write user string
+       i = 0;
+       if (length > sizeof(str))
+               length = sizeof(str);
+       while ((memory_current != memory_end) && (i < length)) {
+
+               *memory_current = str[i];
+
+               memory_current++;
+               cnt++;
+               i++;
+       }
+
+       /// Write trailer
+       static const char trl[2] = {'\r', '\n'};
+       i = 0;
+       while ((memory_current != memory_end) && (i < sizeof(trl))) {
+
+               *memory_current = trl[i];
+
+               memory_current++;
+               cnt++;
+               i++;
+       }
+
+       xSemaphoreGive(memory_mutex);
+       return cnt;
+}
+
+
+// Clears log. Will also stop the show task
+int8_t rpp_sdr_clear()
+{
+       if (!log_enabled)
+               return FAILURE;
+
+       // Stop log show flushing if running
+       if (show_flushing)
+               rpp_sdr_show(FALSE);
+
+       // Check if log is already empty
+       if (memory_current == memory_start)
+               return FAILURE;
+
+       // Reset memory pointer
+       memory_current = memory_start;
+
+       return SUCCESS;
+}
+
+
+// Starts/Stops the task that sends the log to the SCI
+int8_t rpp_sdr_show(boolean_t start)
+{
+       if (!log_enabled)
+               return FAILURE;
+
+       // No change, ignore
+       if (start == show_flushing)
+               return FAILURE;
+
+       // Log flush stop requested
+       if (show_flushing && !start)
+               show_flushing = FALSE;
+
+       // Log flush start requested
+       else {
+               show_flushing = TRUE;
+               xSemaphoreGive(show_semaphore);
+       }
+
+       return SUCCESS;
+}