summaryrefslogblamecommitdiffstats
blob: 7d9f118f66dc7f4d02160098fe6d7cc00deda147 (plain) (tree)
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647



































































































                                                                               
                                                  
                                                         

                                                           






































































































































                                                                                              
              






                                               





                                                                            
         


                                                   





































































































































































































































































































































































































                                                                                            
                                                        
                                                                                     
 
                                                                        





















































                                                                                     


                                                              
                                                      
                                                                                 

                                                                       
 



                                       
















































































































































































                                                                                           
                                                
                                                                         
 
                                                                



























































                                                                               

                                                           

































                                                                                          
                                                                         

                     
                                                                            





















































































































                                                                                                   












                                                                                        






                                                                                                              










                                                                               










































































































































































































































































                                                                                               
/*
 * remote_commands.c
 *
 * Ctools Profiler Server Implementation
 *
 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ 
 * 
 * 
 *  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.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  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 
 *  OWNER 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 <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <errno.h>
#include "error_handler.h"
#include "logging.h"
#include "ctoolsprof.h"
#include "remote_commands.h"
#include "etb_handler.h"


/***************************************************************************** 
 *  Remote RSP Command Definitions 
 *  
 * Note - All remote command handlers return 0 for success
 *        and -1 for failure. Returning 0 causes the command_process() 
 *        function (see command_handler.c) to return rsp message "OK".
 *        Returning -1 causes the command_handler to issue a rsp error packet.
 *        The error handler may be called to generate a local error but these
 *        must be non-fatal so the rsp communications with the client can be
 *        completed. 
 *****************************************************************************/

struct set_element_table_t {
    char * element;
    bool is_valid;
    int value;
};

static struct set_element_table_t set_element_table[] = {
    [OP_MODE] = {"op-mode", false, 0},
    [DURATION] = {"duration", false, 0},
    [START_DELAY] = {"start-delay", false, 0},
    [ETB_ENABLE] = {"etb_enable", false, 0},
    [ETB_MODE] = {"etb", false, 0}
};

typedef enum {
    OP_MODE_TIME,
    OP_MODE_SIGNAL,
    OP_MODE_TRIGGER,
    OP_MODE_LAST
} op_mode_t;

static char * op_mode_table[] = {
    [OP_MODE_TIME] = "time",
    [OP_MODE_SIGNAL] = "signal",
    [OP_MODE_TRIGGER] = "trigger"
};

static char * etb_mode_table[] = {
    [ETB_MODE_ONESHOT_FIXED] = "fixed",
    [ETB_MODE_ONESHOT_CIRCULAR] = "circular",
    [ETB_MODE_DRAIN] = "drain"
};

/* Note these strings must all be the same size */
static const char msg_recording[] = "ctprof recording\n";
static const char msg_stopped[] =   "ctprof stopped__\n";
static const char msg_ready[] =     "ctprof ready____\n";  

/***************************************************************************** 
 *  Internal Function Prototypes 
 *  - See Private Function section below for implementations )
 *****************************************************************************/
void * cTools_memMap(uint32_t map_addr, uint32_t map_size);
void cTools_memUnMap(uint32_t map_addr, uint32_t map_size);
static void respond_ok(char * rbuf, size_t rsp_size);
static void * is_memory_mapped(uint32_t addr, size_t byte_cnt, void ** v_start, size_t * len);

/***************************************************************************** 
 *  Public Functions
 *****************************************************************************/

/***************************************************************************** 
 *  get_set_value()
 *
 *  - Provide the value for the requested element and if the value
 *    is valid(has been set).
 *****************************************************************************/
void get_set_value(set_element_t element, unsigned int *value, bool *is_valid)
{

#if DEBUG
    if (element >= LAST_SET_ELEMENT) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }

    if ((value == NULL) || (is_valid == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    *is_valid = set_element_table[element].is_valid;
    *value = set_element_table[element].value;

}

/***************************************************************************** 
 *  set_command_handler()
 *
 *  - Set a value for the requested element 
 *  - argv definition:
 *       argv[0] should always be "set".
 *       argv[1] is element to set (ie "op_mode")
 *       argv[2] is the string value.
 *   - argv elements are by definition always strings
 *   - Based on the element, the element value is converted to either an
 *     int or an enumeration and stored in set_table[n].value.
 *****************************************************************************/
void set_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    int set_index;
    size_t set_element_cnt = sizeof(set_element_table)
                             /sizeof(struct set_element_table_t);

    LOGFUNC();

#if DEBUG
    if ((argv == NULL) || (rbuf == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    /* Search the command table*/
    for (set_index = 0; set_index < set_element_cnt; set_index++) {
        if (!strcmp(argv[1], set_element_table[set_index].element)) {
            break;
        }
    }
#if DEBUG
    if (set_index == set_element_cnt) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    switch(set_index) {
    int i;
    case OP_MODE:
        for (i = 0; i < OP_MODE_LAST; i++) {
            if (!strcmp(argv[2], op_mode_table[i])) {
                set_element_table[OP_MODE].value = i;
                set_element_table[OP_MODE].is_valid = true;
                break;
            }
        }
#if DEBUG
        if (i == OP_MODE_LAST) {
            err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
        }
#endif
        break;
    case DURATION:
        set_element_table[DURATION].value = atoi(argv[2]);
        set_element_table[DURATION].is_valid = true;
        break;
    case START_DELAY:
        set_element_table[START_DELAY].value = atoi(argv[2]);
        set_element_table[START_DELAY].is_valid = true;
        break;
    case ETB_ENABLE:
        set_element_table[ETB_ENABLE].value = atoi(argv[2]);
        set_element_table[ETB_ENABLE].is_valid = true;
        break;
    case ETB_MODE:
        for (i = 0; i < ETB_MODE_LAST; i++) {
            if (!strcmp(argv[2], etb_mode_table[i])) {
               set_element_table[ETB_MODE].value = i;
               set_element_table[ETB_MODE].is_valid = true;
               break;
            }
        }
#if DEBUG
        if (i == ETB_MODE_LAST) {
            err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
        }
#endif
        break;
    } 

    respond_ok(rbuf, rsp_size);

    LOGMSG1("%s: set %s to %d", __func__, set_element_table[set_index].element,
             set_element_table[set_index].value);
        
}

/***************************************************************************** 
 *  version_command_handler()
 *
 *  - Fill the response buffer with the version of the server. 
 *
 *****************************************************************************/
void version_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    LOGFUNC();

#if DEBUG
    if ((argv == NULL) || (rbuf == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    char * msg;
    int sn_size;

    sn_size = snprintf(rbuf, rsp_size,"server version %d.%d, copyright %d,",
                       g_major_version, g_minor_version, g_copyright_year);

#if DEBUG
        if (sn_size >  rsp_size) {
            err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
        }
#endif

}

/***************************************************************************** 
 *  mmap_command_handler()
 *
 *  - Fill the response buffer with the version of the server. 
 *  - argv definition:
 *       argv[0] should always be "mmap".
 *       argv[1] address string value.
 *       argv[2] size to map value.
 *  - argv elements are by definition always strings
 *
 *****************************************************************************/
void mmap_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    LOGFUNC();

#if DEBUG
    if ((argv == NULL) || (rbuf == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif
    
    uint32_t map_addr = strtoul(argv[1], NULL, 16);
    uint32_t map_size = strtoul(argv[2], NULL, 16);

    LOGMSG1("%s:physical address to map is 0x%x, size %d",__func__,
            map_addr, map_size); 

    cTools_memMap(map_addr, map_size);

    respond_ok(rbuf, rsp_size);

}

/***************************************************************************** 
 *  ummap_command_handler()
 *
 *  - Fill the response buffer with the version of the server. 
 *  - argv definition:
 *       argv[0] should always be "ummap".
 *       argv[1] address string value.
 *       argv[2] size to ummap value.
 *   - argv elements are by definition always strings
 *
 *  - Address from the client is always physical address so must
 *    be converted to virtual address first. 
 *
 *****************************************************************************/
void ummap_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    LOGFUNC();

#if DEBUG
    if (argc < 3) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }

    if ((argv == NULL) || (rbuf == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    uint32_t map_addr = strtoul(argv[1], NULL, 16);
    size_t byte_cnt = strtoul(argv[2], NULL, 16);

    void * v_start_addr;
    size_t len;
    
    void * v_addr = is_memory_mapped(map_addr, byte_cnt, &v_start_addr, &len);

    cTools_memUnMap((uint32_t)v_addr, 0);

    respond_ok(rbuf, rsp_size);

}


/******************************************************************************
 * Commands Specific Definitions for arm and disarm commands
 *
 *****************************************************************************/

struct modcntl_t {
        uint32_t addr;             
        size_t value;             
        struct modcntl_t * prev_modcntl;
        struct modcntl_t * next_modcntl;
};

static struct modcntl_t * arm_table_head = NULL; // Map Object Link List head
static struct modcntl_t * disarm_table_head = NULL; // Map Object Link List head


/***************************************************************************** 
 *  modcntl_add_command_handler()
 *
 *  - Add a arm or disarm command to the arm or disarm 
 *    module control (modcntl_t) link lists 
 *  - argv definition:
 *       argv[0] should always be "arm" or "disarm".
 *       argv[1] address string.
 *       argv[2] value to write for arm or disarm operation
 *   - argv elements are by definition always strings
 *
 *****************************************************************************/
void modcntl_add_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    struct modcntl_t *this_modcntl = NULL;
    struct modcntl_t **modcntl_head = NULL;

    LOGFUNC();

#if DEBUG
    if ((argv == NULL) || (rbuf == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    /* Decide which table to add arm (enable) or disarm (disable) values to. */
    if (!strcmp(argv[0], "arm")) {
        modcntl_head = &arm_table_head;
        LOGMSG1("%s:modcntl add arm address", __func__);

    } else if (!strcmp(argv[0], "disarm")) {
        modcntl_head = &disarm_table_head;
        LOGMSG1("%s:modcntl add disarm address", __func__);
    }
#if DEBUG
    else {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    /* Allocate space for the map element */
    this_modcntl = (struct modcntl_t *)malloc(sizeof(struct modcntl_t));
    if (this_modcntl == NULL) {
       err_handler(ERR_TYPE_LOCAL, ERR_MEM_ALLOC); 
    }

    this_modcntl->addr = strtoul(argv[1], NULL, 16);
    this_modcntl->value = strtoul(argv[2], NULL, 16);
    LOGMSG1("%s:add address 0x%x, value 0x%x", __func__,this_modcntl->addr, 
            this_modcntl->value);

    /* Add element to end of link list */
    this_modcntl->next_modcntl = NULL;

    if (*modcntl_head == NULL) {

        *modcntl_head = this_modcntl;
        this_modcntl->prev_modcntl = NULL;
    } else {
        /* Search for the last element */
        struct modcntl_t * modcntl_element = *modcntl_head;
        while (modcntl_element->next_modcntl != NULL) {
            modcntl_element = modcntl_element->next_modcntl;
        }

        modcntl_element->next_modcntl = this_modcntl;
        this_modcntl->prev_modcntl = modcntl_element;
    }    

    respond_ok(rbuf, rsp_size);

}

/***************************************************************************** 
 *  modcntl_remove_command_handler()
 *
 *  - Add a arm or disarm command to the arm or disarm 
 *    module control (modcntl_t) link lists 
 *  - argv definition:
 *       argv[0] should always be "rm_arm" or "rm_disarm".
 *       argv[1] address string.
 *   - argv elements are by definition always strings
 *
 *****************************************************************************/
void modcntl_remove_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    
    struct modcntl_t *this_modcntl = NULL;
    struct modcntl_t **modcntl_head = NULL;   

    LOGFUNC();

#if DEBUG
    if ((argv == NULL) || (rbuf == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif 
    
    uint32_t addr = strtoul(argv[1], NULL, 16);

    /* Decide which table to remove arm (enable) or disarm (disable) values */
    if (!strcmp(argv[0], "rm_arm")) {
        modcntl_head = &arm_table_head;
        LOGMSG1("%s:remove arm address 0x%x", __func__, addr);

    } else if (!strcmp(argv[0], "rm_disarm")) {
        modcntl_head = &disarm_table_head;
        LOGMSG1("%s:remove disarm address 0x%x", __func__, addr);
    }
#if DEBUG
    else {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }

    if (*modcntl_head == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    this_modcntl = *modcntl_head;
                
    while (this_modcntl->addr != addr) {
        this_modcntl = this_modcntl->next_modcntl;
        if (this_modcntl == NULL) break;        
    }    

#if DEBUG
    if (this_modcntl == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    /* Only element in link list */
    if ((this_modcntl->next_modcntl == NULL) 
        && (this_modcntl->prev_modcntl == NULL)) {
  
        LOGMSG2("%s:only one element in list", __func__);      
        *modcntl_head = NULL;
    }

    /* First element in link list */
    if ((this_modcntl->next_modcntl != NULL) 
        && (this_modcntl->prev_modcntl == NULL)) {

      LOGMSG2("%s:first element in list", __func__); 
      *modcntl_head = this_modcntl->next_modcntl;
      this_modcntl->next_modcntl->prev_modcntl = NULL; 
    }

    /* Last element in link list */
    if ((this_modcntl->next_modcntl == NULL) 
        && (this_modcntl->prev_modcntl != NULL)) {

        LOGMSG2("%s:last element in list", __func__);
        this_modcntl->prev_modcntl->next_modcntl = NULL;
    }

    /* Middle element in link list */
     if ((this_modcntl->next_modcntl != NULL) 
        && (this_modcntl->prev_modcntl != NULL)) {

        LOGMSG2("%s:element in middle of list", __func__);
        this_modcntl->prev_modcntl->next_modcntl = 
                                            this_modcntl->next_modcntl;
        this_modcntl->next_modcntl->prev_modcntl = 
                                            this_modcntl->prev_modcntl;

    }

    free(this_modcntl);

    respond_ok(rbuf, rsp_size);

}

/***************************************************************************** 
 *  modcntl_get_element()
 *
 *  - Retrieve the next address/value pair for a arm or disarm table element. 
 *  - The calling function sets table to either arm_table_head or disarm_table_head 
 *    to start retrieving address/value pairs at the beginning of a table, or NULL
 *    when it wants to  get the next value pair in the list.
 *
 *****************************************************************************/
static struct modcntl_t *next_table_element = NULL;
bool modcntl_get_element(struct modcntl_t *table, uint32_t *addr, uint32_t *value)
{

    LOGFUNC();

#if DEBUG
    if ((addr == NULL) || (value == NULL)) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    if (table != NULL) {
        LOGMSG1("%s:First time so initialize next_table_element",__func__);
        next_table_element = table;
    }

    if (next_table_element != NULL) {
        *addr = next_table_element->addr;
        *value = next_table_element->value;
        LOGMSG1("%s:Found addr 0x%x, value 0x%x",__func__, *addr, *value);
        next_table_element = next_table_element->next_modcntl;
        return true;
    }
    
    return false;
}

/***************************************************************************** 
 *  start_recording()
 *
 * - If not recording and duration_active is true then setup the timer
 *   to generate a signal when the duration is complete, terminating the 
 *   recording session.
 * - If not recording:
 *      - Enable the ETB if required.
 *      - Arm all trace sources.
 *      - Send the recording message to the app if the fifo is enabled.
 *      - Set the global recording state to recording.
 *****************************************************************************/
void start_recording()
{
    unsigned int duration;
    bool duration_valid;

    LOGFUNC();

    if ((global_state.recording != STATE_RECORDING) 
        && (global_state.duration_active == true)) {

        get_set_value(DURATION, &duration, &duration_valid);
        if (duration_valid == false) {
            duration = 0;
        }

        if (duration > 0) {
            int err;
            /* Setup the interval timer */

            struct itimerval duration_timer; //delay;
            struct timeval duration_time, intraval_time;

            LOGMSG1("%s:setting ITIMER_REAL for duration %d", __func__, duration);
            /* Set the delay time: 
             * start_delay is microseconds so divide by 1000 to get seconds
             * and then multiply the remainder by 1000 to get micro seconds remaining
             */
            duration_time.tv_sec = duration/1000;
            duration_time.tv_usec = (duration % 1000) * 1000;

            intraval_time.tv_sec = 0;
            intraval_time.tv_usec = 0;

            duration_timer.it_interval = intraval_time;       /* Set up time for one shot */
            duration_timer.it_value = duration_time;
            
            err = setitimer(ITIMER_REAL, &duration_timer, NULL);

            /* Note the signal handler for setitimer has already been
             * by the call to signal_handler_init() from main.
             */
#if DEBUG
            if (err == -1) {
                err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
            }
#endif
        }
    }

    /* Enable the ETB and start all modules that have an arm command registered */
    if (global_state.recording != STATE_RECORDING) {

        if (global_state.etb_active) {
            LOGMSG1("%s:ETB enabled", __func__);
            etb_enable();
        }


        {
            uint32_t addr, value;
            bool is_valid = modcntl_get_element(arm_table_head, &addr, &value);
            LOGMSG1("%s:Starting armed modules", __func__);
            while (is_valid) {
                LOGMSG1("%s:arming addr 0x%x, with value 0x%x", __func__,addr, value);
                remote_memory_write(addr, sizeof(uint32_t), &value);
                is_valid = modcntl_get_element(NULL, &addr, &value);
            }
        }

        if (g_fifo_enabled) {
            char * broken_pipe_msg = "recording status";
            remote_pipe_write(msg_recording, strlen(msg_recording), broken_pipe_msg);

            LOGMSG1("%s:write to the pipe %s", __func__, msg_recording);
        }

        fprintf(g_stdout, "\r%s:Recording started\n", g_whoami);
        fflush(g_stdout);

        global_state.recording = STATE_RECORDING;

        return;      
    }

#if DEBUG
    err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
#endif    

}

/***************************************************************************** 
 *  stop_recording()
 *
 * - If recording:
 *      - Disarm all trace sources.
 *      - Disable the ETB if required.
 *      - Send the stop recording message to the app if the fifo is enabled.
 *      - Set the global recording state to stopped.
 *****************************************************************************/
void stop_recording()
{
    LOGFUNC();

    if (global_state.recording == STATE_RECORDING) {
    /* Stop all modules that have a disarm command registered */
        uint32_t addr, value;
        bool is_valid = modcntl_get_element(disarm_table_head, &addr, &value);
        while (is_valid) {
            LOGMSG1("%s:disarming addr 0x%x, with value 0x%x", __func__,addr, value);
            remote_memory_write(addr, sizeof(uint32_t), &value);
            is_valid = modcntl_get_element(NULL, &addr, &value);
        }
 
        global_state.recording = STATE_STOPPED;

        /* If we are not draining the ETB then need to send it's data back.
         * Note - While TEST_MODE active must change state to STATE_STOPPED before
         * a non-zero etb element count is reported.
         * Note - regardless of the mode (FIXED or DRAIN) we are going to 
         * add all the current ETB data to the queue.
         */
        if (global_state.etb_active) {
            LOGMSG1("%s:ETB disabled", __func__);
            etb_disable();
            etb_add_queue(ETB_QUEUE_ALL);
        }

        if (g_fifo_enabled) {

            LOGMSG2("%s:write msg_stopped to pipe", __func__);

            char * broken_pipe_msg = "stopped status";
            remote_pipe_write(msg_stopped, strlen(msg_stopped), broken_pipe_msg);

            LOGMSG1("%s:write to the pipe %s", __func__, msg_stopped); 

            if (!g_terminate) {
                g_fifo_enabled = false;
            }

        }

        fprintf(g_stdout, "\r%s:Recording stopped\n", g_whoami);
        fflush(g_stdout);       

        return;
    }           

}

/***************************************************************************** 
 *  start_command_handler()
 *
 *  The client is telling the server it can start the recording process:
 * 
 *  - Initialize the global_state elements based on the operation mode (op-mode).
 *  - If ETB is enabled, process the ETB parameters and setup global_state
 *    etb elements. Configure the ETB.
 *  - If global_state.delay_active is true setup the timer to delay recording,
 *    else start recording.
 *  - if global_state.signal_active is true send the ready message to the app.
 *       
 *****************************************************************************/
void start_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{

    unsigned int op_mode;
    bool op_mode_valid;
    unsigned int duration;
    bool duration_valid;
    unsigned int start_delay;
    bool start_delay_valid; 
    unsigned int etb_value;
    bool etb_value_valid;
   
    LOGFUNC();

#if DEBUG
    if (rbuf == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    if (global_state.recording == STATE_RECORDING) {
#if DEBUG 
        err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
    }
#else
        respond_ok(rbuf, rsp_size);
        return;
    }
#endif 

    /* Initialize the global state elements based on the operation mode. */
    get_set_value(OP_MODE, &op_mode, &op_mode_valid);
    if (op_mode_valid == false) {
        /* The default is OP_MODE_TIME with 0 delay and ~ duration
         * (must send end command to stop) 
         */
        op_mode = OP_MODE_TIME;
    }

    LOGMSG1("%s:start recording with operation mode %s", __func__, op_mode_table[op_mode]);

    global_state.delay_active = false;
    global_state.duration_active = false;
    global_state.signal_active = false;
    global_state.etb_active = false;
    global_state.recording = STATE_WAITING;

    switch (op_mode) {
    case OP_MODE_TIME:
    case OP_MODE_TRIGGER:
        get_set_value(DURATION, &duration, &duration_valid);
        if (duration_valid == false) {
            duration = 0;
        }
        get_set_value(START_DELAY, &start_delay, &start_delay_valid);
        if (start_delay_valid == false) {
            start_delay = 0;
        }
        if (start_delay > 0) {
            global_state.delay_active = true;
        } else {
            global_state.duration_active = true;
        }
        break;
    case OP_MODE_SIGNAL:
        duration = 0;
        start_delay = 0;
        global_state.signal_active = true;
        break;
    }


    LOGMSG1("%s:start with duration %d ms, start delay %d, signal mode %d",
            __func__, duration, start_delay, global_state.signal_active);

    /* If ETB is enabled, process the ETB parameters and setup global_state
     * etb elements. 
     */
    get_set_value(ETB_ENABLE, &etb_value, &etb_value_valid);
    if ((etb_value_valid == true) && (etb_value != 0)) {
        etb_bufmode_t etb_mode;
        global_state.etb_active = true;

        get_set_value(ETB_MODE, &etb_value, &etb_value_valid);
        if (etb_value_valid == true) {
            global_state.etb_mode = etb_value;
        } else {
            global_state.etb_mode = ETB_MODE_ONESHOT_FIXED; 
        }

        switch (global_state.etb_mode) {
        case ETB_MODE_ONESHOT_FIXED:
            etb_mode = ETB_FIXED;
            break;
        case ETB_MODE_ONESHOT_CIRCULAR:
        case ETB_MODE_DRAIN:
            etb_mode = ETB_CIRCULAR;
            break;
        case ETB_MODE_LAST: /* This avoids a compiler warning */
            break;
        }

        /* All requirements for TCI6614 can be handled with SYS_TIETB */
        etb_config(etb_mode, SYS_TIETB);

        LOGMSG1("%s:start with etb enable, etb mode is %s", __func__,
                etb_mode_table[global_state.etb_mode]); 
    } else {
        LOGMSG1("%s:start with etb disabled", __func__);
    }   

    /* If global_state.delay_active is true setup the timer to delay recording. */
    if (global_state.delay_active == true) {
        int err;
        /* Setup the interval timer */

        struct itimerval delay;
        struct timeval delay_time, intraval;

        LOGMSG1("%s:setting ITIMER_REAL for start delay", __func__);
        /* Set the delay time: 
         * start_delay is microseconds so divide by 1000 to get seconds
         * and then multiply the remainder by 1000 to get micro seconds remaining
         */
        delay_time.tv_sec = start_delay/1000;
        delay_time.tv_usec = (start_delay % 1000) * 1000;

        intraval.tv_sec = 0;
        intraval.tv_usec = 0;

        delay.it_interval = intraval;       /* Set up time for one shot */
        delay.it_value = delay_time;
        
        err = setitimer(ITIMER_REAL, &delay, NULL);

        /* Note the signal handler for setitimer has already been
         * by the call to signal_handler_init() from main.
         */
#if DEBUG
        if (err == -1) {
            err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
        }
#endif
        
    } else {
        if (global_state.duration_active == true) {
            start_recording();
        }
    }

    respond_ok(rbuf, rsp_size);

    /* If signal_active is true send the ready message to the app. */
    if ((global_state.signal_active == true) && (g_fifo_enabled)) {
        char * broken_pipe_msg = "ready status";
        remote_pipe_write(msg_ready, strlen(msg_ready), broken_pipe_msg);

        LOGMSG1("%s:write to the pipe %s", __func__, msg_ready);
    }

}

/***************************************************************************** 
 *  end_command_handler()
 *
 *  The client is telling the server to stop recording:
 * 
 *  - If delay_active is true then terminate the timer.
 *  - Call stop_recording().
 *       
 *****************************************************************************/
void end_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    LOGFUNC();

#if DEBUG
    if (rbuf == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    /* If delay_active is true then terminate the timer */
    if (global_state.delay_active == true) {
        int err;
        struct timeval delay_time, intraval;
        struct itimerval delay;

        intraval.tv_sec = 0;
        intraval.tv_usec = 0;
        delay_time.tv_sec = 0;
        delay_time.tv_usec = 0;

        delay.it_interval = intraval;
        delay.it_value = delay_time;

        err = setitimer(ITIMER_REAL, &delay, NULL);

#if DEBUG
        if (err == -1) {
            err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
        }
#endif

        global_state.delay_active = false;
    
    }
    stop_recording();

    respond_ok(rbuf, rsp_size);

}

/***************************************************************************** 
 *  status_command_handler()
 *
 *  The client is requesting status:
 * 
 *  - The status format is:
 *       recording or stopped, etb_bytes_available, wrapped
 *       
 *****************************************************************************/
void status_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    LOGFUNC();

#if DEBUG
    if (rbuf == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    char * msg;
    int sn_size;
    int etb_bytes_avaiable;
    bool is_wrapped = false;

    if (global_state.recording == STATE_RECORDING) {
        msg = "recording";
    }
    else {
        if ((global_state.delay_active == true) || (global_state.signal_active == true)) {
            msg = "recording";
        } else {
            msg = "stopped";
        }
    }

    if (set_element_table[ETB_MODE].is_valid == true) {
        /* etb_data_available returns the number of uint32_t words available*/ 
        etb_bytes_avaiable = etb_status(NULL, &is_wrapped) * sizeof(uint32_t);
    } else {
        etb_bytes_avaiable = 0;
    }

    sn_size = snprintf(rbuf, rsp_size,"%s, %d", msg, etb_bytes_avaiable);

    if (is_wrapped) {
        sn_size += snprintf(rbuf + sn_size, rsp_size - sn_size,", wrapped");
    } 

#if DEBUG
        if (sn_size >  rsp_size) {
            err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
        }
#endif

}

/***************************************************************************** 
 *  debug_command_handler()
 *
 *  This is a dummy command handler that can be used for developing new commands.
 *     
 *****************************************************************************/
void debug_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
    LOGFUNC();

#if DEBUG
    if (rbuf == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    for (int i=0; i < argc; i++) {
        fprintf(g_stdout, "%s:argv[%d] is:%s\n", __func__, i, argv[i]);
    }

    respond_ok(rbuf, rsp_size);

}

/***************************************************************************** 
 *  remote_memory_write()
 *
 *  This function performs the physical memory write.
 *  
 *  - Get the virtual address
 *  - Write the data to the virtual address, 1 32-bit word per access.     
 *****************************************************************************/
int remote_memory_write(uint32_t addr, size_t byte_cnt, uint32_t * pbuf)
{
    LOGFUNC();

#if DEBUG
    if (pbuf == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif
  
    void * v_start_addr;
    size_t len;  
    void * v_addr = is_memory_mapped(addr, byte_cnt, &v_start_addr, &len);
    int err; 
#if DEBUG
    if ((byte_cnt % 4) != 0) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif    
    while (byte_cnt > 0) {
        LOGMSG1("%s:Writing phy addr 0x%x, vir addr 0x%x, data is:0x%x",
                __func__, addr, v_addr, *pbuf);
        *(volatile uint32_t *)v_addr++ = *pbuf++;
        byte_cnt -= 4;
    }

#if DEBUG
    if ( err == -1) {
        err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
    }
#endif 
    return 0;
}

/***************************************************************************** 
 *  remote_memory_read()
 *
 *  This function performs the physical memory read.
 *  
 *  - Get the virtual address
 *  - Read the data from the virtual address, 1 32-bit word per access.     
 *****************************************************************************/
int remote_memory_read(uint32_t addr, size_t byte_cnt, uint32_t * pbuf)
{
    LOGFUNC();

#if DEBUG
    if (pbuf == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    void * v_start_addr;
    size_t len;  
    volatile uint32_t * v_addr = (uint32_t *)is_memory_mapped(addr, byte_cnt, &v_start_addr, &len);
#if DEBUG
    if ((byte_cnt % 4) != 0) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif
   
    while (byte_cnt > 0) {

        *pbuf = *(volatile uint32_t *)v_addr;

        LOGMSG1("%s:Reading phy addr 0x%x, vir addr 0x%x, data is:0x%x",
                __func__, addr, v_addr, *pbuf);  
        byte_cnt -= 4;
        pbuf++;
        v_addr++;
    }

    return 0;
}

/***************************************************************************** 
 *  remote_pipe_write()
 *
 *  This function performs the g_fifo_fd write.
 *    
 *****************************************************************************/
void remote_pipe_write(const char * wr_buf_p, size_t wr_bytecnt, char * broken_pipe_msg)
{
    LOGFUNC();

    while ( wr_bytecnt > 0) {
        size_t rc = write(g_fifo_fd, wr_buf_p, wr_bytecnt);

        if ((rc == -1) && (errno == EPIPE)) {
            if (broken_pipe_msg != NULL) {
                fprintf(g_stdout,"%s:warning - pipe reading end closed when sending %s.\n"                    
                                 "%s:  This can be caused by issuing a client operation before\n"
                                 "%s:  starting the task that opens the pipe's reading end OR\n"
                                 "%s:  early termination of the task that opens the pipe's reading end.\n",
                        g_whoami, broken_pipe_msg, g_whoami, g_whoami, g_whoami);
            }
            break;
        }
        if ((rc == -1) && (errno == EINTR)) {
            continue;
        }
        wr_buf_p += rc;
        wr_bytecnt -= rc;
    } 
}

/***************************************************************************** 
 *  Private Functions
 *****************************************************************************/

/******************************************************************************
 * Commands Specific Definitions for cTools_memMap and cTools_memUnMap 
 *
 *****************************************************************************/
/* Only need to open mem_fd once */
static int mem_fd = 0;

struct map_element_t {
        void * v_map_addr;            /* Virtual address */
        size_t v_map_size;            /* Mapped size in bytes - may be whole number of pages */
        uint32_t phy_addr;              /* Used to request adj_v_addr */ 
        struct map_element_t * prev_map_element;
        struct map_element_t * next_map_element;
};

static struct map_element_t * map_table_head = NULL; // Map Object Link List head

/***************************************************************************** 
 *  cTools_memMap()
 *
 *  This function performs the physical memory mapping to this virtual space.
 *
 *  - Mapped memory blocks are kept in a link list, so first check the list
 *    if the map already exist. This is common if the client is re-started.
 *  - Open /dev/mem if not already open.
 *  - Allocate space for the map element.
 *  - Adjust the size to a whole number of pages.
 *  - Map the physical address to the virtual space. Align the physical address
 *    to a page boundary if necessary.
 *  - Add map element to end of link list.
 *  
 *  Note: Even though this is a private function to ctprof_srv, it is also
 *  used by ETBLib (which is statically linked with ctprof_srv). For this 
 *  reason the function prototype is required to be compatible with
 *  the cToolsHelper version that may be used with ETBLib.
 *****************************************************************************/
void * cTools_memMap(uint32_t map_addr, uint32_t map_size)
{

    struct map_element_t * map_element;
    uint32_t page_size = sysconf(_SC_PAGE_SIZE);

    /* Check if map_addr already mapped. 
     * Could make this more general purpose by checking 
     * if the address is in a mapped range, but this works fine
     * as a simple check of debug ip mapping.
     */
    struct map_element_t * this_map_element = map_table_head;
    while (this_map_element != NULL) {

        if ((this_map_element->phy_addr == map_addr)
            && (this_map_element->v_map_size >= map_size)) {

            LOGMSG1("%s:0x%x already mapped", __func__, map_addr);
            return this_map_element->v_map_addr;
        }
        this_map_element = this_map_element->next_map_element;
    }

    if(mem_fd == 0) {
        mem_fd = open("/dev/mem", O_RDWR | O_SYNC | O_RSYNC );
        if (mem_fd == -1) {
            err_handler(ERR_TYPE_SYSTEM, ERR_MMAP_OPEN);
        }
    }

    /* Allocate space for the map element */
    map_element = (struct map_element_t *)malloc(sizeof(struct map_element_t));
    if (map_element == NULL) {
       err_handler(ERR_TYPE_LOCAL, ERR_MEM_ALLOC); 
    }

    /* Note: the physical address (map_addr) may need to be aligned to a 
     *  PAGE_SIZE and map_size may need to be a multiple of PAGE_SIZE.
     *  This means the virtual address may need to be adjusted by the page mask
     *  to get the same physical address (adj_v_addr).
     */
    if ((map_size % page_size) != 0) {
        map_element->v_map_size = ((map_size / page_size) + 1) * page_size;
    } else {
        map_element->v_map_size = map_size;
    }

    map_element->v_map_addr = mmap(0, map_element->v_map_size, 
                                   PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 
                                   map_addr & ~(map_element->v_map_size -1));

    mlock(map_element->v_map_addr, map_element->v_map_size);

    map_element->phy_addr = map_addr;

    LOGMSG1("%s:phy addr: 0x%08x, phy size: %d", __func__, map_addr, map_size);
    LOGMSG1("%s:mapping phy addr 0x%08x",__func__, 
            map_addr & ~(map_element->v_map_size -1)); 
    LOGMSG1("%s:vir addr: 0x%08x, vir size: %d",__func__, 
            map_element->v_map_addr, map_element->v_map_size);

    if (map_element->v_map_addr == MAP_FAILED) {
       free(map_element);
       /* Fatal so error handler exits */ 
       err_handler(ERR_TYPE_SYSTEM, ERR_MMAP_FAIL); 
    }

    /* Add map element to end of link list */
    map_element->next_map_element = NULL;
    if (map_table_head == NULL) {
        map_table_head = map_element;
        map_element->prev_map_element = NULL;
    } else {
        /* Search for the last element */
        struct map_element_t * this_map_element = map_table_head;
        while (this_map_element->next_map_element != NULL) {
            this_map_element = this_map_element->next_map_element;
        }

        this_map_element->next_map_element = map_element;
        map_element->prev_map_element = this_map_element;
    }

    return map_element->v_map_addr;
}

/***************************************************************************** 
 *  cTools_memUnMap()
 *
 *  This function performs the virtual memory un-mapping.
 *
 *  - Find the virtual address in the link list.
 *  - Unmap the virtual block.
 *  - Remove it from the link list.
 *  
 *  Note: Eventhough this is a private function to ctprof_srv, it is also
 *  used by ETBLib (which is statically linked with ctprof_srv). For this 
 *  reason the function prototype is required to be compatible with
 *  the cToolsHelper version that may be used with ETBLib.
 *****************************************************************************/
void cTools_memUnMap(uint32_t v_map_addr, uint32_t map_size)
{
    LOGFUNC();

    struct map_element_t * map_element;
    /* Search for the element */
    struct map_element_t * this_map_element = map_table_head;
  
#if DEBUG
    if (this_map_element == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    while (this_map_element->v_map_addr != (void *)v_map_addr) {
        this_map_element = this_map_element->next_map_element;
        if (this_map_element == NULL) break;        
    }    

#if DEBUG
    if (this_map_element == NULL) {
        err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
    }
#endif

    int ret_val = munmap(this_map_element->v_map_addr,
                         this_map_element->v_map_size);

    if (ret_val == (int)MAP_FAILED) {
       free(map_element);
       /* Fatal so error handler exits */ 
       err_handler(ERR_TYPE_SYSTEM, ERR_MUNMAP_FAIL); 
    }

    /* Only element in link list */
    if ((this_map_element->next_map_element == NULL)
        && (this_map_element->prev_map_element == NULL)) {
        
        map_table_head = NULL;
        close(mem_fd);
        mem_fd = 0;
    }

    /* First element in link list */
    if ((this_map_element->next_map_element != NULL)
        && (this_map_element->prev_map_element == NULL)) {

      map_table_head = this_map_element->next_map_element;
      map_table_head->prev_map_element = NULL; 
    }

    /* Last element in link list */
    if ((this_map_element->next_map_element == NULL)
        && (this_map_element->prev_map_element != NULL)) {

        this_map_element->prev_map_element->next_map_element = NULL;
    }

    /* Middle element in link list */
    if ((this_map_element->next_map_element != NULL)
        && (this_map_element->prev_map_element != NULL)) {

        this_map_element->prev_map_element->next_map_element = 
                                            this_map_element->next_map_element;
        this_map_element->next_map_element->prev_map_element = 
                                            this_map_element->prev_map_element;

    }

    free(this_map_element);

}

/***************************************************************************** 
 *  is_memory_mapped()
 *
 *  Return virtual version of address if mapped
 *
 *  - Find the physical address in the link list and return it's virtual address.
 *****************************************************************************/
static void * is_memory_mapped(uint32_t addr, size_t byte_cnt, void ** v_start, size_t * len)
{
    LOGFUNC();

    uint32_t page_size = sysconf(_SC_PAGE_SIZE);
    uint32_t page_mask = page_size -1;    

    /*Search map table for match */
    struct map_element_t * this_map_element = map_table_head;
    while (this_map_element != NULL) {

        if ((this_map_element->phy_addr == (addr & ~(this_map_element->v_map_size - 1))) 
            && ((addr + byte_cnt - 1) <= (this_map_element->phy_addr 
            + this_map_element->v_map_size - 1))  ) {

            LOGMSG1("%s:Map element found, returning address 0x%x", __func__, 
                     this_map_element->v_map_addr + (addr & page_mask));
            *v_start = this_map_element->v_map_addr;
            *len = this_map_element->v_map_size;
            return this_map_element->v_map_addr + (addr & page_mask);

        }
        this_map_element = this_map_element->next_map_element;
    }    
#if DEBUG
    err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
#endif
    return 0;
}

/***************************************************************************** 
 *  respond_ok()
 *
 *  Provide the RSP "OK" response.
 *
 *****************************************************************************/
static void respond_ok(char * rbuf, size_t rsp_size)
{
        int sn_size = snprintf(rbuf, rsp_size,"OK");
#if DEBUG
        if (sn_size >  rsp_size) {
            err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
        }
#endif
}