root/usr.sbin/grfconfig/grfconfig.c
/*      $NetBSD: grfconfig.c,v 1.16 2016/02/29 18:59:52 christos Exp $  */

/*-
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Ezra Story and Bernd Ernesti.
 *
 * 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/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1997\
 The NetBSD Foundation, Inc.  All rights reserved.");
#endif /* not lint */

#ifndef lint
__RCSID("$NetBSD: grfconfig.c,v 1.16 2016/02/29 18:59:52 christos Exp $");
#endif /* not lint */

#include <sys/file.h>
#include <sys/ioctl.h>
#include <err.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <amiga/dev/grfioctl.h>

static void print_modeline(FILE *fp, struct grfvideo_mode *, int); 
static void suggest(struct grfvideo_mode *, const char *, const char *);

static struct grf_flag {
        u_short grf_flag_number;
        const char      *grf_flag_name;
} grf_flags[] = {
        {GRF_FLAGS_DBLSCAN,             "doublescan"},
        {GRF_FLAGS_LACE,                "interlace"},
        {GRF_FLAGS_PHSYNC,              "+hsync"},
        {GRF_FLAGS_NHSYNC,              "-hsync"},
        {GRF_FLAGS_PVSYNC,              "+vsync"},
        {GRF_FLAGS_NVSYNC,              "-vsync"},
        {GRF_FLAGS_SYNC_ON_GREEN,       "sync-on-green"},
        {0,                             0}
};

/*
 * Dynamic mode loader for NetBSD/Amiga grf devices.
 */
int
main(int ac, char  **av)
{
        struct  grfvideo_mode gv[1];
        struct  grf_flag *grf_flagp;
        FILE    *fp;
        int     c, y, grffd;
        size_t  i;
        int     lineno = 0;
        int     uplim, lowlim;
        char    rawdata = 0, testmode = 0;
        char    *grfdevice = 0, *ptr;
        char    *modefile = 0;
        char    buf[_POSIX2_LINE_MAX];
        char    *cps[31];
        char    *p;
        const char      *errortext;


        while ((c = getopt(ac, av, "rt")) != -1) {
                switch (c) {
                case 'r':       /* raw output */
                        rawdata = 1;
                        break;
                case 't':       /* test the modefile without setting it */
                        testmode = 1;
                        break;
                default:
                        printf("grfconfig [-r] device [file]\n");
                        return (1);
                }
        }
        ac -= optind;
        av += optind;


        if (ac < 1)
                errx(EXIT_FAILURE, "No grf device specified");
        grfdevice = av[0];

        if (ac >= 2)
                modefile = av[1];

        if ((grffd = open(grfdevice, O_RDWR)) == -1)
                err(EXIT_FAILURE, "Can't open grf device `%s'", grfdevice);

        /* If a mode file is specificied, load it in, don't display any info. */

        if (modefile) {
                if (!(fp = fopen(modefile, "r")))
                        err(EXIT_FAILURE, 
                            "Cannot open mode definition file `%s'", modefile);

                while (fgets(buf, sizeof(buf), fp)) {
                        char *obuf, tbuf[_POSIX2_LINE_MAX], *tbuf2;
                        /*
                         * check for end-of-section, comments, strip off trailing
                         * spaces and newline character.
                         */
                        for (p = buf; isspace((unsigned char)*p); ++p)
                                continue;
                        if (*p == '\0' || *p == '#')
                                continue;
                        for (p = strchr(buf, '\0'); isspace((unsigned char)*--p);)
                                continue;
                        *++p = '\0';

                        obuf = buf;
                        tbuf2 = tbuf;
                        while ((*tbuf2 = *obuf) != '\0') {
                                if (*tbuf2 == '#') {
                                        *tbuf2 = '\0';
                                        break;
                                }
                                if (isupper((unsigned char)*tbuf2)) {
                                        *tbuf2 = tolower((unsigned char)*tbuf2);
                                }
                                obuf++;
                                tbuf2++;
                        }
                        obuf = tbuf;

                        lineno = lineno + 1;

#define SP " \b\t\r\n"
                        memset(cps, 0, sizeof(cps));
                        for (i = 0, ptr = strtok(buf, SP);
                            ptr != NULL && i < __arraycount(cps);
                            i++, ptr = strtok(NULL, SP))
                                cps[i] = ptr;


                        if (i < 14)
                                errx(EXIT_FAILURE, "Too few values in mode "
                                    "definition file: `%s'\n", obuf);

                        gv->pixel_clock = atoi(cps[1]);
                        gv->disp_width  = atoi(cps[2]);
                        gv->disp_height = atoi(cps[3]);
                        gv->depth       = atoi(cps[4]);
                        gv->hblank_start        = atoi(cps[5]);
                        gv->hsync_start = atoi(cps[6]);
                        gv->hsync_stop  = atoi(cps[7]);
                        gv->htotal      = atoi(cps[8]);
                        gv->vblank_start        = atoi(cps[9]);
                        gv->vsync_start = atoi(cps[10]);
                        gv->vsync_stop  = atoi(cps[11]);
                        gv->vtotal      = atoi(cps[12]);

                        if ((y = atoi(cps[0])))
                                gv->mode_num = y;
                        else
                                if (strncasecmp("c", cps[0], 1) == 0) {
                                        gv->mode_num = 255;
                                        gv->depth = 4;
                                } else {
                                        errx(EXIT_FAILURE,
                                            "Illegal mode number: %s", cps[0]);
                                }

                        if ((gv->pixel_clock == 0) ||
                            (gv->disp_width == 0) ||
                            (gv->disp_height == 0) ||
                            (gv->depth == 0) ||
                            (gv->hblank_start == 0) ||
                            (gv->hsync_start == 0) ||
                            (gv->hsync_stop == 0) ||
                            (gv->htotal == 0) ||
                            (gv->vblank_start == 0) ||
                            (gv->vsync_start == 0) ||
                            (gv->vsync_stop == 0) ||
                            (gv->vtotal == 0)) {
                                errx(EXIT_FAILURE, "Illegal value in "
                                    "mode #%d: `%s'", gv->mode_num, obuf);
                        }

                        if (strstr(obuf, "default") != NULL) {
                                gv->disp_flags = GRF_FLAGS_DEFAULT;
                        } else {
                                gv->disp_flags = GRF_FLAGS_DEFAULT;
                                for (grf_flagp = grf_flags;
                                  grf_flagp->grf_flag_number; grf_flagp++) {
                                    if (strstr(obuf, grf_flagp->grf_flag_name) != NULL) {
                                        gv->disp_flags |= grf_flagp->grf_flag_number;
                                    }
                                }
                                if (gv->disp_flags == GRF_FLAGS_DEFAULT)
                                        errx(EXIT_FAILURE, "Your are using a "
                                            "mode file with an obsolete "
                                            "format");
                        }

                        /*
                         * Check for impossible gv->disp_flags:
                         * doublescan and interlace,
                         * +hsync and -hsync
                         * +vsync and -vsync.
                         */
                        errortext = NULL;
                        if ((gv->disp_flags & GRF_FLAGS_DBLSCAN) &&
                            (gv->disp_flags & GRF_FLAGS_LACE))
                                errortext = "Interlace and Doublescan";
                        if ((gv->disp_flags & GRF_FLAGS_PHSYNC) &&
                            (gv->disp_flags & GRF_FLAGS_NHSYNC))
                                errortext = "+hsync and -hsync";
                        if ((gv->disp_flags & GRF_FLAGS_PVSYNC) &&
                            (gv->disp_flags & GRF_FLAGS_NVSYNC))
                                errortext = "+vsync and -vsync";

                        if (errortext != NULL)
                                errx(EXIT_FAILURE, "Illegal flags in "
                                    "mode #%d: `%s' are both defined",
                                    gv->mode_num, errortext);

                        /* Check for old horizontal cycle values */
                        if ((gv->htotal < (gv->disp_width / 4))) {
                                gv->hblank_start *= 8;
                                gv->hsync_start *= 8;
                                gv->hsync_stop *= 8;
                                gv->htotal *= 8;
                                suggest(gv, "horizontal videoclock cycle "
                                    "values", obuf);
                                return EXIT_FAILURE;
                        }

                        /* Check for old interlace or doublescan modes */
                        uplim = gv->disp_height + (gv->disp_height / 4);
                        lowlim = gv->disp_height - (gv->disp_height / 4);
                        if (((gv->vtotal * 2) > lowlim) &&
                            ((gv->vtotal * 2) < uplim)) {
                                gv->vblank_start *= 2;
                                gv->vsync_start *= 2;
                                gv->vsync_stop *= 2;
                                gv->vtotal *= 2;
                                gv->disp_flags &= ~GRF_FLAGS_DBLSCAN;
                                gv->disp_flags |= GRF_FLAGS_LACE;
                                suggest(gv, "vertical values for interlace "
                                    "modes", obuf);
                                return EXIT_FAILURE;
                        } else if (((gv->vtotal / 2) > lowlim) &&
                            ((gv->vtotal / 2) < uplim)) {
                                gv->vblank_start /= 2;
                                gv->vsync_start /= 2;
                                gv->vsync_stop /= 2;
                                gv->vtotal /= 2;
                                gv->disp_flags &= ~GRF_FLAGS_LACE;
                                gv->disp_flags |= GRF_FLAGS_DBLSCAN;
                                suggest(gv, "vertical values for doublescan "
                                    "modes", obuf);
                                return EXIT_FAILURE;
                        }

                        if (testmode == 1) {
                                if (lineno == 1)
                                        printf("num clk wid hi dep hbs "
                                            "hss hse ht vbs vss vse vt "
                                            "flags\n");
                                print_modeline(stdout, gv, 1);
                        } else {
                                gv->mode_descr[0] = 0;
                                if (ioctl(grffd, GRFIOCSETMON, (char *) gv) < 0)
                                        err(EXIT_FAILURE, "bad monitor "
                                            "definition for mode #%d",
                                            gv->mode_num);
                        }
                }
                fclose(fp);
        } else {
                ioctl(grffd, GRFGETNUMVM, &y);
                y += 2;
                for (c = 1; c < y; c++) {
                        c = gv->mode_num = (c != (y - 1)) ? c : 255;
                        if (ioctl(grffd, GRFGETVMODE, gv) < 0)
                                continue;
                        if (rawdata) {
                                print_modeline(stdout, gv, 0);
                                continue;
                        }
                        if (c == 255)
                                printf("Console: ");
                        else
                                printf("%2d: ", gv->mode_num);

                        printf("%dx%d",
                            gv->disp_width,
                            gv->disp_height);

                        if (c != 255)
                                printf("x%d", gv->depth);
                        else
                                printf(" (%dx%d)",
                                    gv->disp_width / 8,
                                    gv->disp_height / gv->depth);

                        printf("\t%ld.%ldkHz @ %ldHz",
                            gv->pixel_clock / (gv->htotal * 1000),
                            (gv->pixel_clock / (gv->htotal * 100)) 
                                % 10,
                            gv->pixel_clock / (gv->htotal * gv->vtotal));
                        printf(" flags:");
                                
                        if (gv->disp_flags == GRF_FLAGS_DEFAULT) {
                                printf(" default\n");
                                continue;
                        }

                        for (grf_flagp = grf_flags;
                            grf_flagp->grf_flag_number; grf_flagp++)
                                if (gv->disp_flags & grf_flagp->grf_flag_number)
                                        printf(" %s", grf_flagp->grf_flag_name);
                        printf("\n");
                }
        }

        close(grffd);
        return EXIT_SUCCESS;
}

static void
suggest(struct  grfvideo_mode *gv, const char *d, const char *s)
{
        warnx("Old and no longer supported %s: %s", d, s);
        warnx("Wrong mode line, this could be a possible good model line:");
        fprintf(stderr, "%s: ", getprogname());
        print_modeline(stderr, gv, 0);
}

static void
print_modeline(FILE *fp, struct grfvideo_mode *gv, int rawflags)
{
        struct  grf_flag *grf_flagp;

        if (gv->mode_num == 255)
                fprintf(fp, "c ");
        else
                fprintf(fp, "%d ", gv->mode_num);

        fprintf(fp, "%ld %d %d %d %d %d %d %d %d %d %d %d",
                gv->pixel_clock,
                gv->disp_width,
                gv->disp_height,
                gv->depth,
                gv->hblank_start,
                gv->hsync_start,
                gv->hsync_stop,
                gv->htotal,
                gv->vblank_start,
                gv->vsync_start,
                gv->vsync_stop,
                gv->vtotal);

        if (rawflags) {
                fprintf(fp, " 0x%.2x\n", gv->disp_flags);
                return;
        }
        if (gv->disp_flags == GRF_FLAGS_DEFAULT) {
                fprintf(fp, " default\n");
                return;
        }

        for (grf_flagp = grf_flags; grf_flagp->grf_flag_number; grf_flagp++)
                if (gv->disp_flags & grf_flagp->grf_flag_number)
                        fprintf(fp, " %s", grf_flagp->grf_flag_name);
        fprintf(fp, "\n");
}