/**
 * @file wrtd-config.c
 *
 * Copyright (c) 2018-2019 CERN (home.cern)
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <libgen.h>
#include <libwrtd.h>
#include <inttypes.h>

#include "libwrtd.h"
#include "libwrtd-private.h"

#include <wrtd-internal.h>


static void disp_id(const char *id)
{
        bool valid;
        unsigned i;

        /* Print.  */
        for (i = 0; i < WRTD_ID_LEN; i++) {
                if (id[i] == 0)
                        break;
                putchar(id[i]);
        }

        /* Check if followed by 0. */
        valid = true;
        for (; i < WRTD_ID_LEN; i++)
                if (id[i] != 0) {
                        valid = false;
                        break;
                }

        if (!valid) {
                printf(" ![");
                for (i = 0; i < WRTD_ID_LEN; i++) {
                        if (i != 0)
                                putchar(' ');
                        printf("%02x", (unsigned char)id[i]);
                }
                printf("]");
        }
}

static void disp_root_config(const struct wrtd_root *root)
{
        int j;

        printf("root:\n");
        printf("  version major: %d, minor: %d\n",
               root->ver_major, root->ver_minor);
        printf("  firmware id: 0x%08x\n", root->fw_id);
        printf("  nbr devices: %d\n", root->nbr_devices);
        for (j = 0; j < root->nbr_devices; j++) {
                printf("    dev %d, channels = %d, direction = %d\n",
                       j, root->devices_nbr_chs[j], root->devices_chs_dir[j]);
        }
        printf("  nbr alarms:  %d\n", root->nbr_alarms);
        printf("  nbr rules:   %d\n", root->nbr_rules);
        printf("  cap flags:   0x%02x\n", root->capabilities);
        printf("  wr state:    %u\n", root->wr_state);
        printf("  log_flags:   0x%02x\n", root->log_flags);
        printf("  freeze_flag: %d\n", root->freeze_flag);
        printf("  rules_addr:  0x%04x\n", root->rules_addr);
        printf("  alarms_addr: 0x%04x\n", root->alarms_addr);
        printf("  log_nbr_discarded: %u\n", root->log_nbr_discarded);
        printf("  log_nbr_busy:      %u\n", root->log_nbr_busy);
        printf("  log_nbr_sent:      %u\n", root->log_nbr_sent);
}

static void disp_alarm(const struct wrtd_alarm *alarm)
{
        printf(" id:      ");
        disp_id(alarm->event.id);
        printf("\n"
               " enabled: %d\n", alarm->enabled);
        printf(" next time: ");
        print_ts(&alarm->event.ts);
        printf("\n"
               " repeat count: %d\n", alarm->repeat_count);
        printf(" period:    ");
        print_ns(alarm->period_ns);
        printf("\n");
}

static void disp_rule_conf(const struct wrtd_rule *rule, unsigned nbr_rules)
{
        const struct wrtd_rule_config *cfg = &rule->conf;
        printf(" id:       ");
        disp_id(cfg->id);
        printf("\n"
               " source:   ");
        disp_id(cfg->source_id);
        printf("\n"
               " dest:     ");
        disp_id(cfg->dest_id);
        printf("\n"
               " dest_cpu: %u", cfg->dest_cpu);
        if (cfg->dest_cpu == WRTD_DEST_CPU_LOCAL)
                printf(" (local)");
        else if (cfg->dest_cpu == WRTD_DEST_CH_NET)
                printf(" (net)");
        printf("\n");
        printf(" dest_ch:  %u\n", cfg->dest_ch);
        printf(" enabled:  %d\n", cfg->enabled);
        printf(" hash: %u, hash_chain: %d\n",
               wrtd_id_hash(cfg->source_id) % nbr_rules,
               cfg->hash_chain);
        printf(" send late:    %d\n", cfg->send_late);
        printf(" repeat count: %d\n", cfg->repeat_count);
        printf(" delay_ns:     ");
        print_ns(cfg->delay_ns);
        printf("\n"
               " hold_off_ns:  ");
        print_ns(cfg->hold_off_ns);
        printf("\n"
               " resync_period_ns: ");
        print_ns(cfg->resync_period_ns);
        printf("\n"
               " resync_factor:  ");
        print_ns(cfg->resync_factor);
        printf("\n");
}

static void disp_rule_stat(const struct wrtd_rule *rule)
{
        const struct wrtd_rule_stats *s = &rule->stat;
        uint64_t lat_ns;
        printf(" rx_events: %u\n", s->rx_events);
        printf(" rx_last:   ");
        print_ts(&s->rx_last);
        printf("\n"
               " tx_events: %u\n", s->tx_events);
        printf(" tx_last:   ");
        print_ts(&s->tx_last);
        printf("\n"
               " lat_max:   %uns\n", s->lat_max_ns);
        printf(" lat_min:   %uns\n", s->lat_min_ns);
        lat_ns = s->lat_lo_ns + ((uint64_t)s->lat_hi_ns << 32);
        printf(" lat_avg:   %"PRIu64"ns/%u = ", lat_ns, s->lat_nbr);
        if (s->lat_nbr == 0)
                printf("NA\n");
        else
                printf("%uns\n", (uint32_t)(lat_ns / s->lat_nbr));
        printf(" period: ");
        if (s->rx_last.seconds == 0)
                printf("NA\n");
        else
                printf("%uns\n", (uint32_t)(s->rx_events / s->rx_last.seconds));
        printf(" miss_holdoff:  %u\n", s->miss_holdoff);
        printf(" miss_late:     %u\n", s->miss_late);
        printf(" miss_nosync:   %u\n", s->miss_nosync);
        printf(" miss_overflow: %u\n", s->miss_overflow);
        printf(" miss_last:    ");
        print_ts(&s->miss_last);
        printf("\n"
               " seq: %u\n", s->seq);
}

static enum wrtd_status disp_cpu_config(struct wrtd_dev *wrtd, int cpu,
                                        int disp_rules, int disp_alarms)
{
        enum wrtd_status res;
        struct wrtd_config_msg msg;
        struct wrtd_root root;
        int j;

        printf("\n"
               "CPU #%d\n", cpu);

        res = wrtd_msg_get_config(wrtd, cpu, &msg, __func__);
        if (res != WRTD_SUCCESS)
                return res;
        printf("sync_flag: %s\n",
               msg.sync_flag ? "ok" : "not synced");
        printf("now: ");
        print_ts(&msg.now);
        printf("\n"
               "root address: 0x%04x\n", msg.root_addr);

        res = wrtd_msg_readw(wrtd, cpu, msg.root_addr,
                             sizeof(struct wrtd_root) / 4,
                             (uint32_t*)&root, __func__);
        WRTD_RETURN_IF_ERROR(res);

        /* Note: assume same representation, same endianness.  */
        disp_root_config(&root);

        if (disp_rules) {
                struct wrtd_rule rule;

                for (j = 0; j < root.nbr_rules; j++) {
                        res = wrtd_msg_readw
                                (wrtd, cpu,
                                 root.rules_addr + j * sizeof(struct wrtd_rule),
                                 sizeof(struct wrtd_rule) / 4,
                                 (uint32_t *)&rule, __func__);
                        WRTD_RETURN_IF_ERROR(res);
                        printf("rule #%d\n", j);
                        disp_rule_conf(&rule, root.nbr_rules);
                }
        }

        if (disp_alarms) {
                struct wrtd_alarm alarm;

                for (j = 0; j < root.nbr_alarms; j++) {
                        res = wrtd_msg_readw
                                (wrtd, cpu,
                                 (root.alarms_addr
                                  + j * sizeof(struct wrtd_alarm)),
                                 sizeof(struct wrtd_alarm) / 4,
                                 (uint32_t *)&alarm, __func__);
                        WRTD_RETURN_IF_ERROR(res);
                        printf("alarm #%d\n", j);
                        disp_alarm(&alarm);
                }
        }
        return WRTD_SUCCESS;
}

static void disp_mt_config(struct wrtd_dev *wrtd)
{
        const struct trtl_config_rom *cfg;
        int i, k;

        cfg = trtl_config_get(wrtd->trtl);

        fprintf(stdout, "signature: 0x%08"PRIx32"\n", cfg->signature);
        fprintf(stdout, "version: 0x%08"PRIx32"\n", cfg->version);
        fprintf(stdout, "clock-freq: %"PRId32"\n", cfg->clock_freq);
        fprintf(stdout, "flags: 0x%"PRIx32"\n", cfg->flags);
        fprintf(stdout, "application-id: 0x%08"PRIx32"\n", cfg->app_id);
        fprintf(stdout, "shm-size (32bit words): %"PRId32"\n", cfg->smem_size);
        fprintf(stdout, "cpus:\n");

        for (i = 0; i < cfg->n_cpu; ++i) {
                fprintf(stdout, "\t- index: %d\n", i);
                fprintf(stdout, "\t  mem-size (32bit words): %d\n", cfg->mem_size[i]);
                fprintf(stdout, "\t  hmq:\n");
                for (k = 0; k < cfg->n_hmq[i]; ++k) {
                        uint32_t sizes = cfg->hmq[i][k].sizes;

                        fprintf(stdout, "\t\t- index: %d\n", k);
                        fprintf(stdout, "\t\t  width-header: %"PRId32"\n",
                                TRTL_CONFIG_ROM_MQ_SIZE_HEADER(sizes));
                        fprintf(stdout, "\t\t  width-payload: %"PRId32"\n",
                                TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(sizes));
                        fprintf(stdout, "\t\t  depth: %"PRId32"\n",
                                TRTL_CONFIG_ROM_MQ_SIZE_ENTRIES(sizes));
                        fprintf(stdout, "\t\t  endpoint-id: %"PRId32"\n",
                                cfg->hmq[i][k].endpoint_id);
                }
                fprintf(stdout, "\t  rmq:\n");
                for (k = 0; k < cfg->n_rmq[i]; ++k) {
                        uint32_t sizes = cfg->rmq[i][k].sizes;

                        fprintf(stdout, "\t\t- index: %d\n", k);
                        fprintf(stdout, "\t\t  width-header: %"PRId32"\n",
                                TRTL_CONFIG_ROM_MQ_SIZE_HEADER(sizes));
                        fprintf(stdout, "\t\t  width-payload: %"PRId32"\n",
                                TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(sizes));
                        fprintf(stdout, "\t\t  depth: %"PRId32"\n",
                                TRTL_CONFIG_ROM_MQ_SIZE_ENTRIES(sizes));
                        fprintf(stdout, "\t\t  endpoint-id: %"PRId32"\n",
                                cfg->hmq[i][k].endpoint_id);
                }
        }
}

static enum wrtd_status wrtd_cmd_dump_all(struct wrtd_dev *wrtd,
                                          int argc, char *argv[])
{
        enum wrtd_status status;
        int cpu;

        disp_mt_config(wrtd);

        for (cpu = 0; cpu < wrtd->nbr_cpus; cpu++) {
                status = disp_cpu_config(wrtd, cpu, 1, 1);
                WRTD_RETURN_IF_ERROR(status);
        }

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_dump_mt_config(struct wrtd_dev *wrtd,
                                                int argc, char *argv[])
{
        disp_mt_config(wrtd);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_dump_cpus(struct wrtd_dev *wrtd,
                                           int argc, char *argv[])
{
        enum wrtd_status status;
        int cpu;

        for (cpu = 0; cpu < wrtd->nbr_cpus; cpu++) {
                status = disp_cpu_config(wrtd, cpu, 0, 0);
                WRTD_RETURN_IF_ERROR(status);
        }

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_dump_rules(struct wrtd_dev *wrtd,
                                            int argc, char *argv[])
{
        enum wrtd_status status;
        unsigned i;

        status = wrtd_fill_rules(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        for (i = 0; i < wrtd->nbr_rules; i++) {
                struct wrtd_lib_rule *r = &wrtd->rules[i];
                if (wrtd_id_null(r->rule.conf.id))
                        continue;
                printf("cpu#%u rule %u:\n", r->cpu, r->local_idx);
                disp_rule_conf(&r->rule, wrtd->roots[r->cpu].nbr_rules);
                disp_rule_stat(&r->rule);
        }

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_dump_alarms(struct wrtd_dev *wrtd,
                                             int argc, char *argv[])
{
        enum wrtd_status status;
        unsigned i;

        status = wrtd_fill_alarms(wrtd, __func__);
        WRTD_RETURN_IF_ERROR(status);

        for (i = 0; i < wrtd->nbr_alarms; i++) {
                struct wrtd_lib_alarm *a = &wrtd->alarms[i];
                if (wrtd_id_null(a->alarm.event.id))
                        continue;
                printf("cpu#%u alarm %u:\n", a->cpu, a->idx);
                disp_alarm(&a->alarm);
        }

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_sys_time(struct wrtd_dev *wrtd,
                                          int argc, char *argv[])
{
        struct wrtd_tstamp t;
        enum wrtd_status res;

        res = wrtd_get_attr_tstamp(wrtd, WRTD_GLOBAL_REP_CAP_ID,
                                   WRTD_ATTR_SYS_TIME, &t);
        WRTD_RETURN_IF_ERROR(res);

        print_ts(&t);
        printf("\n");

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_add_alarm(struct wrtd_dev *wrtd,
                                           int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "missing alarm name\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        /* Create.  */
        status = wrtd_add_alarm(wrtd, id);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_set_alarm(struct wrtd_dev *wrtd,
                                           int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;
        uint64_t delay;
        uint64_t setup;
        uint64_t period;
        uint32_t count;
        struct wrtd_tstamp ts;
        int i;

        if (argc < 1) {
                fprintf(stderr, "alarm id missing\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];
        delay = 0;
        period = 0;
        count = 0;
        setup = 0;

        for (i = 1; i < argc; i++) {
                if (strcmp(argv[i], "-t") == 0) {
                        i++;
                        if (i == argc) {
                                fprintf(stderr, "missing argument for -t");
                                return WRTD_SUCCESS;
                        }
                        if (parse_delay(argv[i], &delay) != 0) {
                                fprintf(stderr, "bad delay\n");
                                /* Don't print another message.  */
                                return WRTD_SUCCESS;
                        }
                }
                else if (strcmp(argv[i], "-s") == 0) {
                        i++;
                        if (i == argc) {
                                fprintf(stderr, "missing argument for -s");
                                return WRTD_SUCCESS;
                        }
                        if (parse_delay(argv[i], &setup) != 0) {
                                fprintf(stderr, "bad setup\n");
                                /* Don't print another message.  */
                                return WRTD_SUCCESS;
                        }
                }
                else if (strcmp(argv[i], "-p") == 0) {
                        i++;
                        if (i == argc) {
                                fprintf(stderr, "missing argument for -p");
                                return WRTD_SUCCESS;
                        }
                        if (parse_delay(argv[i], &period) != 0) {
                                fprintf(stderr, "bad period\n");
                                /* Don't print another message.  */
                                return WRTD_SUCCESS;
                        }
                }
                else if (strcmp(argv[i], "-c") == 0) {
                        char *endptr;
                        i++;
                        if (i == argc) {
                                fprintf(stderr, "missing argument for -c");
                                return WRTD_SUCCESS;
                        }
                        count = strtoul(argv[i], &endptr, 10);
                        if (*endptr != 0) {
                                fprintf(stderr, "bad count\n");
                                /* Don't print another message.  */
                                return WRTD_SUCCESS;
                        }
                }
                else {
                        fprintf(stderr, "unknown option %s\n", argv[i]);
                        /* Don't print another message.  */
                        return WRTD_SUCCESS;
                }
        }


        /* Set time.  */
        status = wrtd_get_attr_tstamp(wrtd, WRTD_GLOBAL_REP_CAP_ID,
                                      WRTD_ATTR_SYS_TIME, &ts);
        WRTD_RETURN_IF_ERROR(status);
        ts_add_ps(&ts, delay);

        status = wrtd_set_attr_tstamp(wrtd, id, WRTD_ATTR_ALARM_TIME, &ts);
        WRTD_RETURN_IF_ERROR(status);

        ts_sub_ps(&ts, setup);
        status = wrtd_set_attr_tstamp(wrtd, id,
                                      WRTD_ATTR_ALARM_TIME, &ts);
        WRTD_RETURN_IF_ERROR(status);

        /* Set repeat count.  */
        status = wrtd_set_attr_int32(wrtd, id,
                                     WRTD_ATTR_ALARM_REPEAT_COUNT, count);
        WRTD_RETURN_IF_ERROR(status);

        /* Set period.  */
        ts.seconds = 0;
        ts.ns = 0;
        ts.frac = 0;
        ts_add_ps(&ts, period);
        status = wrtd_set_attr_tstamp(wrtd, id, WRTD_ATTR_ALARM_PERIOD, &ts);
        WRTD_RETURN_IF_ERROR(status);

        /* Enable.  */
        status = wrtd_set_attr_bool(wrtd, id, WRTD_ATTR_ALARM_ENABLED, true);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_disable_alarm(struct wrtd_dev *wrtd,
                                               int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "incorrect arguments\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        /* Disable.  */
        status = wrtd_set_attr_bool(wrtd, id, WRTD_ATTR_ALARM_ENABLED, false);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_del_alarm(struct wrtd_dev *wrtd,
                                           int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "incorrect arguments\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        status = wrtd_remove_alarm(wrtd, id);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_list_alarms(struct wrtd_dev *wrtd,
                                             int argc, char *argv[])
{
        enum wrtd_status status;
        int32_t count;
        char id[WRTD_ID_LEN];
        int32_t i;

        status = wrtd_get_attr_int32 (wrtd, WRTD_GLOBAL_REP_CAP_ID,
                                      WRTD_ATTR_ALARM_COUNT, &count);
        WRTD_RETURN_IF_ERROR(status);

        printf ("%d alarms:\n", count);
        for (i = 0; i < count; i++) {
                bool enabled;

                status = wrtd_get_alarm_name(wrtd, i, sizeof(id), id);
                WRTD_RETURN_IF_ERROR(status);

                printf(" %.*s", WRTD_ID_LEN, id);

                status = wrtd_get_attr_bool(wrtd, id, WRTD_ATTR_ALARM_ENABLED,
                                            &enabled);
                WRTD_RETURN_IF_ERROR(status);
                printf(" %s\n", enabled ? "enabled" : "disabled");
        }
        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_add_rule(struct wrtd_dev *wrtd,
                                          int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "missing rule name\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        /* Create.  */
        status = wrtd_add_rule(wrtd, id);
        WRTD_RETURN_IF_ERROR(status);


        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_set_rule(struct wrtd_dev *wrtd,
                                          int argc, char *argv[])
{
        unsigned i;
        struct wrtd_tstamp ts;
        const char *id;
        enum wrtd_status status;
        uint64_t delay;
        bool has_delay;

        has_delay = false;

        if (argc < 1) {
                fprintf(stderr, "rule id missing\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        for (i = 1; i < argc; i++) {
                if (argv[i][0] != '-')
                        break;
                if (strcmp(argv[i], "-d") == 0) {
                        i++;
                        if (i == argc) {
                                fprintf(stderr, "missing argument for -d");
                                return WRTD_SUCCESS;
                        }
                        if (parse_delay(argv[i], &delay) != 0) {
                                fprintf(stderr, "bad delay\n");
                                /* Don't print another message.  */
                                return WRTD_SUCCESS;
                        }
                        has_delay = true;
                }
        }

        if (i + 2 != argc) {
                fprintf(stderr, "missing <src> <dest>\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        status = wrtd_set_attr_string(wrtd, id,
                                      WRTD_ATTR_RULE_SOURCE, argv[i + 0]);
        WRTD_RETURN_IF_ERROR(status);

        status = wrtd_set_attr_string(wrtd, id,
                                      WRTD_ATTR_RULE_DESTINATION, argv[i + 1]);
        WRTD_RETURN_IF_ERROR(status);

        /* Set delay.  */
        if (has_delay) {
                ts.seconds = 0;
                ts.ns = 0;
                ts.frac = 0;
                ts_add_ps(&ts, delay);
                status = wrtd_set_attr_tstamp(wrtd, id,
                                              WRTD_ATTR_RULE_DELAY, &ts);
                WRTD_RETURN_IF_ERROR(status);
        }

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_enable_disable_rule(struct wrtd_dev *wrtd,
                                                     int argc, char *argv[],
                                                     bool val)
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "missing rule name\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        status = wrtd_set_attr_bool(wrtd, id,
                                    WRTD_ATTR_RULE_ENABLED, val);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_enable_rule(struct wrtd_dev *wrtd,
                                             int argc, char *argv[])
{
        return wrtd_cmd_enable_disable_rule(wrtd, argc, argv, true);
}

static enum wrtd_status wrtd_cmd_disable_rule(struct wrtd_dev *wrtd,
                                              int argc, char *argv[])
{
        return wrtd_cmd_enable_disable_rule(wrtd, argc, argv, false);
}

static enum wrtd_status wrtd_cmd_list_rules(struct wrtd_dev *wrtd,
                                            int argc, char *argv[])
{
        enum wrtd_status status;
        int32_t count;
        int32_t i;

        status = wrtd_get_attr_int32 (wrtd, WRTD_GLOBAL_REP_CAP_ID,
                                      WRTD_ATTR_RULE_COUNT, &count);
        WRTD_RETURN_IF_ERROR(status);

        printf ("%d rules:\n", count);
        for (i = 0; i < count; i++) {
                bool enabled;
                char rid[WRTD_ID_LEN + 1];
                char id[WRTD_ID_LEN + 1];

                status = wrtd_get_rule_name(wrtd, i, sizeof(rid), rid);
                WRTD_RETURN_IF_ERROR(status);

                printf(" %s", rid);

                status = wrtd_get_attr_bool(wrtd, rid, WRTD_ATTR_RULE_ENABLED,
                                            &enabled);
                WRTD_RETURN_IF_ERROR(status);
                printf(" %s", enabled ? "enabled" : "disabled");

                status = wrtd_get_attr_string
                        (wrtd, rid, WRTD_ATTR_RULE_SOURCE, sizeof(id), id);
                WRTD_RETURN_IF_ERROR(status);
                printf(" %s", id);

                status = wrtd_get_attr_string
                        (wrtd, rid, WRTD_ATTR_RULE_DESTINATION, sizeof(id), id);
                WRTD_RETURN_IF_ERROR(status);
                printf(" -> %s\n", id);
        }
        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_del_rule(struct wrtd_dev *wrtd,
                                          int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "incorrect arguments\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        status = wrtd_remove_rule(wrtd, id);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_reset_rule_stats(struct wrtd_dev *wrtd,
                                                  int argc, char *argv[])
{
        const char *id;
        enum wrtd_status status;

        if (argc != 1) {
                fprintf(stderr, "missing rule name\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        id = argv[0];

        status = wrtd_reset_rule_stats(wrtd, id);

        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_set_log(struct wrtd_dev *wrtd,
                                         int argc, char *argv[])
{
        enum wrtd_status status;
        bool val;

        if (argc != 1) {
                fprintf(stderr, "need argument\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }
        if (strcmp(argv[0], "on") == 0
            || strcmp(argv[0], "enable") == 0)
                val = true;
        else if (strcmp(argv[0], "off") == 0
                 || strcmp(argv[0], "disable") == 0)
                val = false;
        else {
                fprintf(stderr, "incorrect argument\n");
                /* Don't print another message.  */
                return WRTD_SUCCESS;
        }

        status = wrtd_set_attr_bool(wrtd, WRTD_GLOBAL_REP_CAP_ID,
                                    WRTD_ATTR_EVENT_LOG_ENABLED, val);
        WRTD_RETURN_IF_ERROR(status);

        return WRTD_SUCCESS;
}

static enum wrtd_status wrtd_cmd_clear_log(struct wrtd_dev *wrtd,
                                           int argc, char *argv[])
{
        return wrtd_clear_event_log_entries(wrtd);
}

static struct wrtd_commands cmds[] = {
        { "dump-all", "", "shows wrtd config",
          wrtd_cmd_dump_all },
        { "dump-mt-config", "", "shows wrtd MT config",
          wrtd_cmd_dump_mt_config },
        { "dump-cpu", "", "shows wrtd cpu",
          wrtd_cmd_dump_cpus },
        { "dump-rules", "", "shows wrtd rules",
          wrtd_cmd_dump_rules },
        { "dump-alarms", "", "shows wrtd alarms",
          wrtd_cmd_dump_alarms },
        { "time", "", "shows system time",
          wrtd_cmd_sys_time },
        { "add-alarm", "<name>", "create an alarm",
          wrtd_cmd_add_alarm },
        { "set-alarm", "<name> [-t <time>] [-p <period>] [-c <count>]",
          "set an alarm",
          wrtd_cmd_set_alarm },
        { "del-alarm", "<name>", "delete an alarm",
          wrtd_cmd_del_alarm },
        { "disable-alarm", "<name>", "disable an alarm",
          wrtd_cmd_disable_alarm },
        { "list-alarms", "", "list all alarms",
          wrtd_cmd_list_alarms },
        { "add-rule", "<name>", "create a rule",
          wrtd_cmd_add_rule },
        { "set-rule", "<name> [-d <delay>] <src> <dest>",
          "set rule source and dest",
          wrtd_cmd_set_rule },
        { "list-rules", "", "list all rules",
          wrtd_cmd_list_rules },
        { "enable-rule", "<name>", "enable a rule",
          wrtd_cmd_enable_rule },
        { "disable-rule", "<name>", "disable a rule",
          wrtd_cmd_disable_rule },
        { "del-rule", "<name>", "delete a rule",
          wrtd_cmd_del_rule },
        { "reset-rule-stats", "<name", "reset rule statistics",
          wrtd_cmd_reset_rule_stats },
        { "set-log", "on/off", "enable or disable log",
          wrtd_cmd_set_log },
        { "clear-log", "", "clear pending log entries",
          wrtd_cmd_clear_log },
#if 0
        { "state", "", "shows input state",
          wrtd_cmd_state },
        { "enable", "", "enable the input",
          wrtd_cmd_enable },
        { "disable", "", "disable the input",
          wrtd_cmd_disable },
        { "deadtime", "<number>", "sets the dead time in pico-seconds",
          wrtd_cmd_set_dead_time },
        { "delay", "<number>", "sets the input delay in pico-seconds",
          wrtd_cmd_set_delay },
        { "mode", "<mode>", "sets triggering mode (see Trigger Modes)",
          wrtd_cmd_set_mode },
        { "assign", "<trig-id>", "assigns a trigger (see Trigger ID)",
          wrtd_cmd_assign },
        { "unassign", "", "un-assigns the currently assigned trigger",
          wrtd_cmd_unassign },
        { "arm", "", "arms the input",
          wrtd_cmd_arm },
        { "disarm", "", "disarms the input",
          wrtd_cmd_disarm },
        { "reset", "", "resets statistics counters",
          wrtd_cmd_reset },
        { "global_reset", "", "resets global statistics counters",
          wrtd_cmd_global_reset },
        { "swtrig", "", "sends a software trigger",
          wrtd_cmd_sw_trigger },
        { "log_level", "<level>", "set logging level (see Log Levels)",
          wrtd_cmd_log_level },
#endif
        { NULL }
};

static void help(void)
{
        fprintf(stderr, "wrtd-config -D DEVICE -- COMMAND [cmd-options]\n\n");
        fprintf(stderr, "It configures a White-Rabbit Trigger-Distribution node\n\n");
        fprintf(stderr, "-D\tdevice id\n");
        help_commands(cmds);
        exit(1);
}

int main(int argc, char *argv[])
{
        struct wrtd_dev *wrtd;
        const char *dev_name = NULL;
        enum wrtd_status status;
        char *cmd = NULL;
        char c;
        int i;
        char error_description[256];
        enum wrtd_status error_code;

        while ((c = getopt (argc, argv, "+hD:c:")) != -1) {
                switch (c) {
                case 'h':
                case '?':
                        help();
                        break;
                case 'D':
                        dev_name = optarg;
                        break;
                }
        }

        if (dev_name == NULL || optind == argc) {
                help();
                exit(1);
        }

        /* Find command.  */
        cmd = argv[optind++];
        for (i = 0; cmds[i].handler; i++) {
                if(!strcmp(cmds[i].name, cmd))
                        break;
        }
        if (cmds[i].handler == NULL) {
                fprintf(stderr, "unknown command '%s', try -h\n", cmd);
                return 1;
        }

        /* Open.  */
        status = wrtd_init(dev_name, 0, NULL, &wrtd);
        if (status != WRTD_SUCCESS) {
                fprintf(stderr,
                        "Cannot open WRTD %s, status=0x%08x (errno=%s)\n",
                        dev_name, status, strerror(errno));
                return 1;
        }

        status = cmds[i].handler(wrtd, argc - optind, argv + optind);
        if (status != WRTD_SUCCESS) {
                wrtd_get_error(wrtd, &error_code, 256, error_description);
                fprintf(stderr,
                        "Error while executing command '%s': %s\n",
                        cmd, error_description);
        }

        wrtd_close(wrtd);

        return 0;
}
