/*      $NetBSD: memswitch.c,v 1.17 2019/02/08 08:55:35 isaki Exp $     */

/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Minoura Makoto.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/

/* memswitch.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/ioctl.h>

#ifndef SRAMDEBUG
#include <machine/sram.h>
#else
/*
* SRAMDEBUG -- works on other (faster) platforms;
*   store in a regular file instead of actual non-volatile static RAM.
*/
#include <sys/stat.h>
#define PATH_RAMFILE "/tmp/sramfile"
#endif

#include "memswitch.h"

char *progname;
int nflag = 0;
u_int8_t *current_values = 0;
u_int8_t *modified_values = 0;

static void
usage(void)
{
       fprintf(stderr, "usage: %s -a\n", progname);
       fprintf(stderr, "       %s [-h] variable ...\n", progname);
       fprintf(stderr, "       %s -w variable=value ...\n", progname);
       fprintf(stderr, "       %s [-rs] filename\n", progname);
       exit(1);
}

int
main(int argc, char *argv[])
{
       int ch;
       enum md {
               MD_NONE, MD_WRITE, MD_HELP, MD_SHOWALL, MD_SAVE, MD_RESTORE
       } mode = MD_NONE;

       progname = argv[0];

       while ((ch = getopt(argc, argv, "whanrs")) != -1) {
               switch (ch) {
               case 'w':       /* write */
                       mode = MD_WRITE;
                       break;
               case 'h':
                       mode = MD_HELP;
                       break;
               case 'a':
                       mode = MD_SHOWALL;
                       break;
               case 'n':
                       nflag = 1;
                       break;
               case 's':
                       mode = MD_SAVE;
                       break;
               case 'r':
                       mode = MD_RESTORE;
                       break;
               }
       }
       argc -= optind;
       argv += optind;

       switch (mode) {
       case MD_NONE:
               if (argc == 0)
                       usage();
               while (argv[0]) {
                       show_single(argv[0]);
                       argv++;
               }
               break;
       case MD_SHOWALL:
               if (argc)
                       usage();
               show_all();
               break;
       case MD_WRITE:
               if (argc == 0)
                       usage();
               while (argv[0]) {
                       modify_single (argv[0]);
                       argv++;
               }
               flush();
               break;
       case MD_HELP:
               if (argc == 0)
                       usage();
               while (argv[0]) {
                       help_single(argv[0]);
                       argv++;
               }
               break;
       case MD_SAVE:
               if (argc != 1)
                       usage();
               save(argv[0]);
               break;
       case MD_RESTORE:
               if (argc != 1)
                       usage();
               restore(argv[0]);
               break;

       }

       return 0;
}

void
show_single(const char *name)
{
       int i;
       int n = 0;
       char fullname[50];
       char valuestr[MAXVALUELEN];

       for (i = 0; i < number_of_props; i++) {
               snprintf(fullname, sizeof(fullname), "%s.%s",
                        properties[i].class, properties[i].node);
               if (strcmp(name, fullname) == 0 || strcmp(name, properties[i].class) == 0) {
                       properties[i].print(&properties[i], valuestr);
                       if (!nflag)
                               printf("%s=%s\n", fullname, valuestr);
                       n++;
               }
       }
       if (n == 0) {
               errx(1, "No such %s: %s", strstr(name, ".")?"property":"class", name);
       }

       return;
}

void
show_all(void)
{
       int i;
       char valuestr[MAXVALUELEN];

       for (i = 0; i < number_of_props; i++) {
               properties[i].print(&properties[i], valuestr);
               if (!nflag)
                       printf("%s.%s=",
                              properties[i].class, properties[i].node);
               printf("%s\n", valuestr);
       }

       return;
}

void
modify_single(const char *expr)
{
       int i;
       char *buf;
       char *p;
       const char *class;
       const char *node;
       const char *value;
       char valuestr[MAXVALUELEN];

       buf = strdup(expr);
       if (buf == NULL)
               err(EXIT_FAILURE, "strdup failed");

       p = buf;
       for (class = p; *p; p++) {
               if (*p == '.') {
                       *p++ = '\0';
                       break;
               }
       }

       for (node = p; *p; p++) {
               if (*p == '=') {
                       *p++ = '\0';
                       break;
               }
       }

       value = p;

       if (class[0] == '\0' || node[0] == '\0' || value[0] == '\0')
               errx(1, "Invalid expression: %s", expr);

       for (i = 0; i < number_of_props; i++) {
               if (strcmp(properties[i].class, class) == 0 &&
                   strcmp(properties[i].node, node) == 0) {
                       if (properties[i].parse(&properties[i], value) < 0) {
                               /* error: do nothing */
                       } else {
                               properties[i].print(&properties[i], valuestr);
                               printf("%s.%s -> %s\n", class, node, valuestr);
                       }
                       break;
               }
       }
       if (i >= number_of_props) {
               errx(1, "No such property: %s.%s", class, node);
       }

       free(buf);
}

void
help_single(const char *name)
{
       int i;
       char fullname[50];
       char valuestr[MAXVALUELEN];

       for (i = 0; i < number_of_props; i++) {
               snprintf(fullname, sizeof(fullname), "%s.%s",
                   properties[i].class, properties[i].node);
               if (strcmp(name, fullname) == 0) {
                       properties[i].print(&properties[i], valuestr);
                       if (!nflag)
                               printf("%s=", fullname);
                       printf("%s\n", valuestr);
                       printf("%s", properties[i].descr);
                       break;
               }
       }
       if (i >= number_of_props) {
               errx(1, "No such property: %s", name);
       }

       return;
}

void
alloc_modified_values(void)
{
       if (current_values == 0)
               alloc_current_values();
       modified_values = malloc(256);
       if (modified_values == 0)
               err(1, "malloc");
       memcpy(modified_values, current_values, 256);
}

void
alloc_current_values(void)
{
#ifndef SRAMDEBUG
       int i;
       int sramfd = 0;
       struct sram_io buffer;

       current_values = malloc(256);
       if (current_values == 0)
               err(1, "malloc");

       sramfd = open(_PATH_DEVSRAM, O_RDONLY);
       if (sramfd < 0)
               err(1, "Opening %s", _PATH_DEVSRAM);

       /* Assume SRAM_IO_SIZE = n * 16. */
       for (i = 0; i < 256; i += SRAM_IO_SIZE) {
               buffer.offset = i;
               if (ioctl(sramfd, SIOGSRAM, &buffer) < 0)
                       err(1, "ioctl");
               memcpy(&current_values[i], buffer.sram, SRAM_IO_SIZE);
       }

       close(sramfd);
#else
       int i;
       int fd;
       struct stat st;

       current_values = malloc(256);
       if (current_values == 0)
               err(1, "malloc");

       fd = open(PATH_RAMFILE, O_RDONLY);
       if (fd < 0 && errno == ENOENT) {
               modified_values = malloc(256);
               if (modified_values == 0)
                       err(1, NULL);
               for (i = 0; i < number_of_props; i++) {
                       properties[i].modified_value
                           = properties[i].default_value;
                       properties[i].modified = 1;
                       properties[i].flush(&properties[i]);
               }

               fd = creat(PATH_RAMFILE, 0666);
               if (fd < 0)
                       err(1, "Creating %s", PATH_RAMFILE);
               if (write(fd, modified_values, 256) != 256)
                       err(1, "Writing %s", PATH_RAMFILE);
               close(fd);
               free(modified_values);
               modified_values = 0;

               fd = open(PATH_RAMFILE, O_RDONLY);
       }
       if (fd < 0)
               err(1, "Opening %s", PATH_RAMFILE);
       if (fstat(fd, &st) < 0)
               err(1, "fstat");
       if (st.st_size != 256)
               errx(1, "PANIC! INVALID RAMFILE");
       if (read(fd, current_values, 256) != 256)
               err(1, "reading %s", PATH_RAMFILE);
       close(fd);
#endif

       properties[PROP_MAGIC1].fill(&properties[PROP_MAGIC1]);
       properties[PROP_MAGIC2].fill(&properties[PROP_MAGIC2]);
       if ((properties[PROP_MAGIC1].current_value.longword != MAGIC1) ||
           (properties[PROP_MAGIC2].current_value.longword != MAGIC2))
               errx(1, "PANIC! INVALID MAGIC");
}

void
flush(void)
{
       int i;
       int sramfd = 0;
#ifndef SRAMDEBUG
       struct sram_io buffer;
#endif

       for (i = 0; i < number_of_props; i++) {
               if (properties[i].modified)
                       properties[i].flush(&properties[i]);
       }

       if (modified_values == 0)
               /* Not modified at all. */
               return;

#ifndef SRAMDEBUG
       /* Assume SRAM_IO_SIZE = n * 16. */
       for (i = 0; i < 256; i += SRAM_IO_SIZE) {
               if (memcmp(&current_values[i], &modified_values[i],
                          SRAM_IO_SIZE) == 0)
                       continue;

               if (sramfd == 0) {
                       sramfd = open(_PATH_DEVSRAM, O_RDWR);
                       if (sramfd < 0)
                               err(1, "Opening %s", _PATH_DEVSRAM);
               }
               buffer.offset = i;
               memcpy(buffer.sram, &modified_values[i], SRAM_IO_SIZE);
               if (ioctl(sramfd, SIOPSRAM, &buffer) < 0)
                       err(1, "ioctl");
       }
#else
       sramfd = open(PATH_RAMFILE, O_WRONLY);
       if (sramfd < 0)
               err(1, "Opening %s", PATH_RAMFILE);
       if (write(sramfd, modified_values, 256) != 256)
               err(1, "Writing %s", PATH_RAMFILE);
#endif

       if (sramfd != 0)
               close(sramfd);

       return;
}

int
save(const char *name)
{
#ifndef SRAMDEBUG
       int fd;

       alloc_current_values();

       if (strcmp(name, "-") == 0)
               fd = 1;         /* standard output */
       else {
               fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
               if (fd < 0)
                       err(1, "Opening output file");
       }

       if (write(fd, current_values, 256) != 256)
               err(1, "Writing output file");

       if (fd != 1)
               close(fd);
#else
       fprintf(stderr, "Skipping save...\n");
#endif

       return 0;
}

int
restore(const char *name)
{
#ifndef SRAMDEBUG
       int sramfd, fd, i;
       struct sram_io buffer;

       modified_values = malloc(256);
       if (modified_values == 0)
               err(1, "Opening %s", _PATH_DEVSRAM);

       if (strcmp(name, "-") == 0)
               fd = 0;         /* standard input */
       else {
               fd = open(name, O_RDONLY);
               if (fd < 0)
                       err(1, "Opening input file");
       }

       if (read(fd, modified_values, 256) != 256)
               err(1, "Reading input file");

       if (fd != 0)
               close(fd);

       sramfd = open(_PATH_DEVSRAM, O_RDWR);
       if (sramfd < 0)
               err(1, "Opening %s", _PATH_DEVSRAM);

       /* Assume SRAM_IO_SIZE = n * 16. */
       for (i = 0; i < 256; i += SRAM_IO_SIZE) {
               buffer.offset = i;
               memcpy(buffer.sram, &modified_values[i], SRAM_IO_SIZE);
               if (ioctl(sramfd, SIOPSRAM, &buffer) < 0)
                       err(1, "ioctl");
       }

       close(sramfd);
#else
       fprintf(stderr, "Skipping restore...\n");
#endif

       return 0;
}