root/sys/arch/atari/stand/bootpref/bootpref.c
/*      $NetBSD: bootpref.c,v 1.6 2009/03/17 00:18:40 he Exp $  */
/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Julian Coleman.
 *
 * 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.
 */
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
#include <sys/mman.h>
#include "bootpref.h"

static void     usage(void);
static int      openNVRAM(void);
static void     closeNVRAM(int);
static u_char   readNVRAM(int, int);
static void     writeNVRAM(int, int, u_char);
static void     getNVpref(int, u_char[]);
static void     setNVpref(int, u_char[], int, int);
static void     showOS(u_char);
static void     showLang(u_char);
static void     showKbdLang(u_char);
static void     showDateFmt(u_char);
static void     showDateSep(u_char);
static void     showVideo2(u_char);
static void     showVideo1(u_char, u_char);
static int      checkOS(u_char *, char *);
static int      checkLang(u_char *, char *);
static int      checkKbdLang(u_char *, char *);
static int      checkInt(u_char *, char *, int, int);
static int      checkDateFmt(u_char *, char *);
static void     checkDateSep(u_char *, char *);
static int      checkColours(u_char *, char *);

#define SET_OS          0x001
#define SET_LANG        0x002
#define SET_KBDLANG     0x004
#define SET_HOSTID      0x008
#define SET_DATIME      0x010
#define SET_DATESEP     0x020
#define SET_BOOTDLY     0x040
#define SET_VID1        0x080
#define SET_VID2        0x100

#define ARRAY_OS        0
#define ARRAY_LANG      1
#define ARRAY_KBDLANG   2
#define ARRAY_HOSTID    3
#define ARRAY_DATIME    4
#define ARRAY_DATESEP   5
#define ARRAY_BOOTDLY   6
#define ARRAY_VID1      7
#define ARRAY_VID2      8

static const char       nvrdev[] = PATH_NVRAM;

int
main (int argc, char *argv[])
{
        int     c, set = 0, verbose = 0;
        int     fd;
        u_char  bootpref[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
        u_char  check_hour = 0, check_video1 = 0, check_video2 = 0;

        fd = openNVRAM ();
        bootpref[ARRAY_VID2] = readNVRAM (fd, NVRAM_VID2);
        bootpref[ARRAY_VID1] = readNVRAM (fd, NVRAM_VID1);
        /* parse options */
        while ((c = getopt (argc, argv, "Vb:d:k:l:s:f:12e:c:nptv48oOxXiI")) != -1) {
                switch (c) {
                case 'V':
                        verbose = 1;
                        break;
                case 'b':
                        if (checkOS (&bootpref[ARRAY_OS], optarg))
                                set |= SET_OS;
                        else
                                usage ();
                        break;
                case 'd':
                        if (checkInt (&bootpref[ARRAY_BOOTDLY], optarg,
                           0, 255))
                                set |= SET_BOOTDLY;
                        else
                                usage ();
                        break;
                case 'k':
                        if (checkKbdLang (&bootpref[ARRAY_KBDLANG], optarg))
                                set |= SET_KBDLANG;
                        else
                                usage ();
                        break;
                case 'l':
                        if (checkLang (&bootpref[ARRAY_LANG], optarg))
                                set |= SET_LANG;
                        else
                                usage ();
                        break;
                case 's':
                        if (checkInt (&bootpref[ARRAY_HOSTID], optarg,
                            0, 7))
                                set |= SET_HOSTID;
                        else
                                usage ();
                        break;
                case 'f':
                        if (checkDateFmt (&bootpref[ARRAY_DATIME], optarg))
                                set |= SET_DATIME;
                        else
                                usage ();
                        break;
                case '1':
                        if (check_hour & DATIME_24H) {
                                usage();
                        } else {
                                bootpref[ARRAY_DATIME] &= ~DATIME_24H;
                                set |= SET_DATIME;
                                check_hour |= DATIME_24H;
                        }
                        break;
                case '2':
                        if (check_hour & DATIME_24H) {
                                usage();
                        } else {
                                bootpref[ARRAY_DATIME] |= DATIME_24H;
                                set |= SET_DATIME;
                                check_hour |= DATIME_24H;
                        }
                        break;
                case 'e':
                        checkDateSep (&bootpref[ARRAY_DATESEP], optarg);
                        set |= SET_DATESEP;
                        break;
                case 'c':
                        if (checkColours (&bootpref[ARRAY_VID2], optarg))
                                set |= SET_VID2;
                        else
                                usage ();
                        break;
                case 'n':
                        if (check_video2 & VID2_PAL) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] &= ~VID2_PAL;
                                set |= SET_VID2;
                                check_video2 |= VID2_PAL;
                        }
                        break;
                case 'p':
                        if (check_video2 & VID2_PAL) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] |= VID2_PAL;
                                set |= SET_VID2;
                                check_video2 |= VID2_PAL;
                        }
                        break;
                case 't':
                        if (check_video2 & VID2_VGA) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] &= ~VID2_VGA;
                                set |= SET_VID2;
                                check_video2 |= VID2_VGA;
                        }
                        break;
                case 'v':
                        if (check_video2 & VID2_VGA) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] |= VID2_VGA;
                                set |= SET_VID2;
                                check_video2 |= VID2_VGA;
                        }
                        break;
                case '4':
                        if (check_video2 & VID2_80CLM) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] &= ~VID2_80CLM;
                                set |= SET_VID2;
                                check_video2 |= VID2_80CLM;
                        }
                        break;
                case '8':
                        if (check_video2 & VID2_80CLM) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] |= VID2_80CLM;
                                set |= SET_VID2;
                                check_video2 |= VID2_80CLM;
                        }
                        break;
                case 'o':
                        if (check_video2 & VID2_OVERSCAN) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] |= VID2_OVERSCAN;
                                set |= SET_VID2;
                                check_video2 |= VID2_OVERSCAN;
                        }
                        break;
                case 'O':
                        if (check_video2 & VID2_OVERSCAN) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] &= ~VID2_OVERSCAN;
                                set |= SET_VID2;
                                check_video2 |= VID2_OVERSCAN;
                        }
                        break;
                case 'x':
                        if (check_video2 & VID2_COMPAT) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] |= VID2_COMPAT;
                                set |= SET_VID2;
                                check_video2 |= VID2_COMPAT;
                        }
                        break;
                case 'X':
                        if (check_video2 & VID2_COMPAT) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID2] &= ~VID2_COMPAT;
                                set |= SET_VID2;
                                check_video2 |= VID2_COMPAT;
                        }
                        break;
                case 'i':
                        if (check_video1 & VID1_INTERLACE) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID1] |= VID1_INTERLACE;
                                set |= SET_VID1;
                                check_video1 |= VID1_INTERLACE;
                        }
                        break;
                case 'I':
                        if (check_video1 & VID1_INTERLACE) {
                                usage();
                        } else {
                                bootpref[ARRAY_VID1] &= ~VID1_INTERLACE;
                                set |= SET_VID1;
                                check_video1 |= VID1_INTERLACE;
                        }
                        break;
                default:
                        usage ();
                }
        }
        if (optind != argc) {
                usage ();
        }
        if (set) {
                setNVpref (fd, bootpref, set, verbose);
        } else {
                getNVpref (fd, bootpref);
        }
        closeNVRAM (fd);
        return (EXIT_SUCCESS);
}

static void
usage (void)
{
        fprintf (stderr,
                "usage: bootpref [-V] [-b os] [-d delay] [-k kbd] [-l lang] "
                "[-s id]\n"
                "\t[-f fmt] [-1] [-2] [-e sep]\n"
                "\t[-c colours] [-n] [-p] [-t] [-v] [-4] [-8]\n"
                "\t[-o] [-O] [-x] [-X] [-i] [-I]\n");
        exit (EXIT_FAILURE);
}

static int
openNVRAM ()
{
        int fd;

        if ((fd = open (nvrdev, O_RDWR)) < 0) {
                err (EXIT_FAILURE, "%s", nvrdev);
        }
        return (fd);
}

static void
closeNVRAM (int fd)
{
        if (close (fd) < 0) {
                err (EXIT_FAILURE, "%s", nvrdev);
        }
}

static u_char
readNVRAM (int fd, int pos)
{
        u_char val;

        if (lseek(fd, (off_t)pos, SEEK_SET) != pos) {
                err(EXIT_FAILURE, "%s", nvrdev);
        }
        if (read (fd, &val, (size_t)1) != 1) {
                err(EXIT_FAILURE, "%s", nvrdev);
        }
        return (val);
}

static void
writeNVRAM (int fd, int pos, u_char val)
{
        if (lseek(fd, (off_t)pos, SEEK_SET) != pos) {
                err(EXIT_FAILURE, "%s", nvrdev);
        }
        if (write (fd, &val, (size_t)1) != 1) {
                err(EXIT_FAILURE, "%s", nvrdev);
        }
}

static void
getNVpref (int fd, u_char bootpref[])
{
        /* Boot OS */
        printf ("Boot OS is ");
        showOS (readNVRAM (fd, NVRAM_BOOTPREF));
        /* Boot Delay */
        printf ("Boot delay is %d seconds\n", readNVRAM (fd, NVRAM_BOOTDLY));
        /* TOS Language */
        printf ("Language is ");
        showLang (readNVRAM (fd, NVRAM_LANG));
        /* Keyboard Language */
        printf ("Keyboard is ");
        showKbdLang (readNVRAM (fd, NVRAM_KBDLANG));
        /* SCSI Host ID */
        printf ("SCSI host ID is ");
        if (readNVRAM (fd, NVRAM_HOSTID) & HOSTID_VALID) {
                printf ("%d\n", readNVRAM (fd, NVRAM_HOSTID) ^ HOSTID_VALID);
        } else {
                printf ("invalid");
        }
        /* Date format/separator */
        printf ("Date format is ");
        showDateFmt (readNVRAM (fd, NVRAM_DATIME));
        printf ("Date separator is ");
        showDateSep (readNVRAM (fd, NVRAM_DATESEP));
        /* Video */
        printf ("Video is (0x%02x, 0x%02x) :\n", readNVRAM (fd, NVRAM_VID2),
            readNVRAM (fd, NVRAM_VID1));
        showVideo2 (readNVRAM (fd, NVRAM_VID2));
        showVideo1 (readNVRAM (fd, NVRAM_VID1), readNVRAM (fd, NVRAM_VID2));
}

static void
setNVpref (int fd, u_char bootpref[], int set, int verbose)
{
        /* Boot OS */
        if (set & SET_OS) {
                writeNVRAM (fd, NVRAM_BOOTPREF, bootpref[ARRAY_OS]);
                if (verbose) {
                        printf ("Boot OS set to ");
                        showOS (readNVRAM (fd, NVRAM_BOOTPREF));
                }
        }
        /* Boot Delay */
        if (set & SET_BOOTDLY) {
                writeNVRAM (fd, NVRAM_BOOTDLY, bootpref[ARRAY_BOOTDLY]);
                if (verbose) {
                        printf ("Boot delay set to %d seconds\n", readNVRAM (fd,
                            NVRAM_BOOTDLY));
                }
        }
        /* TOS Language */
        if (set & SET_LANG) {
                writeNVRAM (fd, NVRAM_LANG, bootpref[ARRAY_LANG]);
                if (verbose) {
                        printf ("Language set to ");
                        showLang (readNVRAM (fd, NVRAM_LANG));
                }
        }
        /* Keyboard Language */
        if (set & SET_KBDLANG) {
                writeNVRAM (fd, NVRAM_KBDLANG, bootpref[ARRAY_KBDLANG]);
                if (verbose) {
                        printf ("Keyboard set to ");
                        showKbdLang (readNVRAM (fd, NVRAM_KBDLANG));
                }
        }
        /* SCSI Host ID */
        if (set & SET_HOSTID) {
                writeNVRAM (fd, NVRAM_HOSTID, bootpref[ARRAY_HOSTID] |
                    HOSTID_VALID);
                if (verbose) {
                        printf ("SCSI host ID set to ");
                        printf ("%d\n", readNVRAM (fd, NVRAM_HOSTID) ^
                                HOSTID_VALID);
                }
        }
        /* Date format/separator */
        if (set & SET_DATIME) {
                writeNVRAM (fd, NVRAM_DATIME, bootpref[ARRAY_DATIME]);
                if (verbose) {
                        printf ("Date format set to ");
                        showDateFmt (readNVRAM (fd, NVRAM_DATIME));
                        printf ("\n");
                }
        }
        if (set & SET_DATESEP) {
                writeNVRAM (fd, NVRAM_DATESEP, bootpref[ARRAY_DATESEP]);
                if (verbose) {
                        printf ("Date separator set to ");
                        showDateSep (readNVRAM (fd, NVRAM_DATESEP));
                }
        }
        /* Video */
        if ((set & SET_VID2) || (set & SET_VID1)) {
                if (set & SET_VID2) {
                        writeNVRAM (fd, NVRAM_VID2, bootpref[ARRAY_VID2]);
                }
                if (set & SET_VID1) {
                        writeNVRAM (fd, NVRAM_VID1, bootpref[ARRAY_VID1]);
                }
                if (verbose) {
                        printf ("Video set to (0x%02x, 0x%02x) :\n",
                            readNVRAM (fd, NVRAM_VID2),
                            readNVRAM (fd, NVRAM_VID1));
                        showVideo2 (readNVRAM (fd, NVRAM_VID2));
                        showVideo1 (readNVRAM (fd, NVRAM_VID1),
                            readNVRAM (fd, NVRAM_VID2));
                }
        }
}

static void
showOS (u_char bootos)
{
        switch (bootos) {
        case BOOTPREF_NETBSD:
                printf ("NetBSD");
                break;
        case BOOTPREF_TOS:
                printf ("TOS");
                break;
        case BOOTPREF_MAGIC:
                printf ("MAGIC");
                break;
        case BOOTPREF_LINUX:
                printf ("Linux");
                break;
        case BOOTPREF_SYSV:
                printf ("System V");
                break;
        case BOOTPREF_NONE:
                printf ("none");
                break;
        default:
                printf ("unknown");
                break;
        }
        printf (" (0x%x).\n", bootos);
}

static void
showLang (u_char lang)
{
        switch (lang) {
        case LANG_USA:
        case LANG_GB:
                printf ("English");
                break;
        case LANG_D:
                printf ("German");
                break;
        case LANG_FR:
                printf ("French");
                break;
        case LANG_ESP:
                printf ("Spanish");
                break;
        case LANG_I:
                printf ("Italian");
                break;
        default:
                printf ("unknown");
                break;
        }
        printf (" (0x%x).\n", lang);
}

static void
showKbdLang (u_char lang)
{
        switch (lang) {
        case KBDLANG_USA:
                printf ("American");
                break;
        case KBDLANG_D:
                printf ("German");
                break;
        case KBDLANG_FR:
                printf ("French");
                break;
        case KBDLANG_GB:
                printf ("British");
                break;
        case KBDLANG_ESP:
                printf ("Spanish");
                break;
        case KBDLANG_I:
                printf ("Italian");
                break;
        case KBDLANG_CHF:
                printf ("Swiss (French)");
                break;
        case KBDLANG_CHD:
                printf ("Swiss (German)");
                break;
        default:
                printf ("unknown");
                break;
        }
        printf (" (0x%x).\n", lang);
}

static void
showDateFmt (u_char fmt)
{
        if (fmt & DATIME_24H) {
                printf ("24 hour clock, ");
        } else {
                printf ("12 hour clock, ");
        }
        switch (fmt & ~DATIME_24H) {
        case DATIME_MMDDYY:
                printf ("MMDDYY");
                break;
        case DATIME_DDMMYY:
                printf ("DDMMYY");
                break;
        case DATIME_YYMMDD:
                printf ("YYMMDD");
                break;
        case DATIME_YYDDMM:
                printf ("YYDDMM");
                break;
        default:
                printf ("unknown");
                break;
        }
        printf (" (0x%02x)\n", fmt);
}

static void
showDateSep (u_char sep)
{
        if (sep) {
                if (sep >= 0x20) {
                        printf ("\"%c\" ", sep);
                }
        } else {
                printf ("\"/\" ");
        }
        printf ("(0x%02x)\n", sep);
}

static void
showVideo2 (u_char vid2)
{
        u_char colours;

        colours = vid2 & 0x07;
        printf ("\t");
        switch (colours) {
        case VID2_2COL:
                printf ("2");
                break;
        case VID2_4COL:
                printf ("4");
                break;
        case VID2_16COL:
                printf ("16");
                break;
        case VID2_256COL:
                printf ("256");
                break;
        case VID2_65535COL:
                printf ("65535");
                break;
        }
        printf (" colours, ");
        if (vid2 & VID2_80CLM) {
                printf ("80");
        } else {
                printf ("40");
        }
        printf (" column, ");
        if (vid2 & VID2_VGA) {
                printf ("VGA");
        } else {
                printf ("TV");
        }
        printf (", ");
        if (vid2 & VID2_PAL) {
                printf ("PAL\n");
        } else {
                printf ("NTSC\n");
        }
        printf ("\tOverscan ");
        if (vid2 & VID2_OVERSCAN) {
                printf ("on\n");
        } else {
                printf ("off\n");
        }
        printf ("\tST compatibility ");
        if (vid2 & VID2_COMPAT) {
                printf ("on\n");
        } else {
                printf ("off\n");
        }
}

static void
showVideo1 (u_char vid1, u_char vid2)
{
        if (vid2 & VID2_VGA) {
                printf ("\tDouble line ");
                if (vid1 & VID1_INTERLACE) {
                        printf ("on");
                } else {
                        printf ("off");
                }
        } else {
                printf ("\tInterlace ");
                if (vid1 & VID1_INTERLACE) {
                        printf ("on");
                } else {
                        printf ("off");
                }
        }
        printf ("\n");
}

static int
checkOS (u_char *val, char *str)
{
        if (!strncasecmp (str, "ne", 2)) {
                *val = BOOTPREF_NETBSD;
                return (1);
        }
        if (!strncasecmp (str, "t", 1)) {
                *val = BOOTPREF_TOS;
                return (1);
        }
        if (!strncasecmp (str, "m", 1)) {
                *val = BOOTPREF_MAGIC;
                return (1);
        }
        if (!strncasecmp (str, "l", 1)) {
                *val = BOOTPREF_LINUX;
                return (1);
        }
        if (!strncasecmp (str, "s", 1)) {
                *val = BOOTPREF_SYSV;
                return (1);
        }
        if (!strncasecmp (str, "no", 2)) {
                *val = BOOTPREF_NONE;
                return (1);
        }
        return (0);
}

static int
checkLang (u_char *val, char *str)
{
        if (!strncasecmp (str, "e", 1)) {
                *val = LANG_GB;
                return (1);
        }
        if (!strncasecmp (str, "g", 1)) {
                *val = LANG_D;
                return (1);
        }
        if (!strncasecmp (str, "f", 1)) {
                *val = LANG_FR;
                return (1);
        }
        if (!strncasecmp (str, "s", 1)) {
                *val = LANG_ESP;
                return (1);
        }
        if (!strncasecmp (str, "i", 1)) {
                *val = LANG_I;
                return (1);
        }
        return (0);
}

static int
checkKbdLang (u_char *val, char *str)
{
        if (!strncasecmp (str, "a", 1)) {
                *val = KBDLANG_USA;
                return (1);
        }
        if (!strncasecmp (str, "g", 1)) {
                *val = KBDLANG_D;
                return (1);
        }
        if (!strncasecmp (str, "f", 1)) {
                *val = KBDLANG_FR;
                return (1);
        }
        if (!strncasecmp (str, "b", 1)) {
                *val = KBDLANG_GB;
                return (1);
        }
        if (!strncasecmp (str, "sp", 2)) {
                *val = KBDLANG_ESP;
                return (1);
        }
        if (!strncasecmp (str, "i", 1)) {
                *val = KBDLANG_I;
                return (1);
        }
        if (!strncasecmp (str, "swiss f", 7) || !strncasecmp (str, "sw f", 4)) {
                *val = KBDLANG_CHF;
                return (1);
        }
        if (!strncasecmp (str, "swiss g", 7) || !strncasecmp (str, "sw g", 4)) {
                *val = KBDLANG_CHD;
                return (1);
        }
        return (0);
}

static int
checkInt (u_char *val, char *str, int min, int max)
{
        int num;
        if (1 == sscanf (str, "%d", &num) && num >= min && num <= max) {
                *val = num;
                return (1);
        }
        return (0);
}

static int
checkDateFmt (u_char *val, char *str)
{
        if (!strncasecmp (str, "m", 1)) {
                *val |= DATIME_MMDDYY;
                return (1);
        }
        if (!strncasecmp (str, "d", 1)) {
                *val |= DATIME_DDMMYY;
                return (1);
        }
        if (!strncasecmp (str, "yym", 3)) {
                *val |= DATIME_YYMMDD;
                return (1);
        }
        if (!strncasecmp (str, "yyd", 3)) {
                *val |= DATIME_YYDDMM;
                return (1);
        }
        return (0);
}

static void
checkDateSep (u_char *val, char *str)
{
        if (str[0] == '/') {
                *val = 0;
        } else {
                *val = str[0];
        }
}
        
static int
checkColours (u_char *val, char *str)
{
        *val &= ~0x07;
        if (!strncasecmp (str, "6", 1)) {
                *val |= VID2_65535COL;
                return (1);
        }
        if (!strncasecmp (str, "25", 2)) {
                *val |= VID2_256COL;
                return (1);
        }
        if (!strncasecmp (str, "1", 1)) {
                *val |= VID2_16COL;
                return (1);
        }
        if (!strncasecmp (str, "4", 1)) {
                *val |= VID2_4COL;
                return (1);
        }
        if (!strncasecmp (str, "2", 1)) {
                *val |= VID2_2COL;
                return (1);
        }
        return (0);
}