1/*	$NetBSD: grfconfig.c,v 1.16 2016/02/29 18:59:52 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Ezra Story and Bernd Ernesti.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__COPYRIGHT("@(#) Copyright (c) 1997\
35 The NetBSD Foundation, Inc.  All rights reserved.");
36#endif /* not lint */
37
38#ifndef lint
39__RCSID("$NetBSD: grfconfig.c,v 1.16 2016/02/29 18:59:52 christos Exp $");
40#endif /* not lint */
41
42#include <sys/file.h>
43#include <sys/ioctl.h>
44#include <err.h>
45#include <ctype.h>
46#include <limits.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51
52#include <amiga/dev/grfioctl.h>
53
54static void print_modeline(FILE *fp, struct grfvideo_mode *, int);
55static void suggest(struct grfvideo_mode *, const char *, const char *);
56
57static struct grf_flag {
58	u_short	grf_flag_number;
59	const char	*grf_flag_name;
60} grf_flags[] = {
61	{GRF_FLAGS_DBLSCAN,		"doublescan"},
62	{GRF_FLAGS_LACE,		"interlace"},
63	{GRF_FLAGS_PHSYNC,		"+hsync"},
64	{GRF_FLAGS_NHSYNC,		"-hsync"},
65	{GRF_FLAGS_PVSYNC,		"+vsync"},
66	{GRF_FLAGS_NVSYNC,		"-vsync"},
67	{GRF_FLAGS_SYNC_ON_GREEN,	"sync-on-green"},
68	{0,				0}
69};
70
71/*
72 * Dynamic mode loader for NetBSD/Amiga grf devices.
73 */
74int
75main(int ac, char  **av)
76{
77	struct	grfvideo_mode gv[1];
78	struct	grf_flag *grf_flagp;
79	FILE	*fp;
80	int	c, y, grffd;
81	size_t  i;
82	int	lineno = 0;
83	int	uplim, lowlim;
84	char	rawdata = 0, testmode = 0;
85	char	*grfdevice = 0, *ptr;
86	char	*modefile = 0;
87	char	buf[_POSIX2_LINE_MAX];
88	char	*cps[31];
89	char	*p;
90	const char	*errortext;
91
92
93	while ((c = getopt(ac, av, "rt")) != -1) {
94		switch (c) {
95		case 'r':	/* raw output */
96			rawdata = 1;
97			break;
98		case 't':	/* test the modefile without setting it */
99			testmode = 1;
100			break;
101		default:
102			printf("grfconfig [-r] device [file]\n");
103			return (1);
104		}
105	}
106	ac -= optind;
107	av += optind;
108
109
110	if (ac < 1)
111		errx(EXIT_FAILURE, "No grf device specified");
112	grfdevice = av[0];
113
114	if (ac >= 2)
115		modefile = av[1];
116
117	if ((grffd = open(grfdevice, O_RDWR)) == -1)
118		err(EXIT_FAILURE, "Can't open grf device `%s'", grfdevice);
119
120	/* If a mode file is specificied, load it in, don't display any info. */
121
122	if (modefile) {
123		if (!(fp = fopen(modefile, "r")))
124			err(EXIT_FAILURE,
125			    "Cannot open mode definition file `%s'", modefile);
126
127		while (fgets(buf, sizeof(buf), fp)) {
128			char *obuf, tbuf[_POSIX2_LINE_MAX], *tbuf2;
129			/*
130			 * check for end-of-section, comments, strip off trailing
131			 * spaces and newline character.
132			 */
133			for (p = buf; isspace((unsigned char)*p); ++p)
134				continue;
135			if (*p == '\0' || *p == '#')
136				continue;
137			for (p = strchr(buf, '\0'); isspace((unsigned char)*--p);)
138				continue;
139			*++p = '\0';
140
141			obuf = buf;
142			tbuf2 = tbuf;
143			while ((*tbuf2 = *obuf) != '\0') {
144				if (*tbuf2 == '#') {
145					*tbuf2 = '\0';
146					break;
147				}
148				if (isupper((unsigned char)*tbuf2)) {
149					*tbuf2 = tolower((unsigned char)*tbuf2);
150				}
151				obuf++;
152				tbuf2++;
153			}
154			obuf = tbuf;
155
156			lineno = lineno + 1;
157
158#define SP " \b\t\r\n"
159			memset(cps, 0, sizeof(cps));
160			for (i = 0, ptr = strtok(buf, SP);
161			    ptr != NULL && i < __arraycount(cps);
162			    i++, ptr = strtok(NULL, SP))
163				cps[i] = ptr;
164
165
166			if (i < 14)
167				errx(EXIT_FAILURE, "Too few values in mode "
168				    "definition file: `%s'\n", obuf);
169
170			gv->pixel_clock	= atoi(cps[1]);
171			gv->disp_width	= atoi(cps[2]);
172			gv->disp_height	= atoi(cps[3]);
173			gv->depth	= atoi(cps[4]);
174			gv->hblank_start	= atoi(cps[5]);
175			gv->hsync_start	= atoi(cps[6]);
176			gv->hsync_stop	= atoi(cps[7]);
177			gv->htotal	= atoi(cps[8]);
178			gv->vblank_start	= atoi(cps[9]);
179			gv->vsync_start	= atoi(cps[10]);
180			gv->vsync_stop	= atoi(cps[11]);
181			gv->vtotal	= atoi(cps[12]);
182
183			if ((y = atoi(cps[0])))
184				gv->mode_num = y;
185			else
186				if (strncasecmp("c", cps[0], 1) == 0) {
187					gv->mode_num = 255;
188					gv->depth = 4;
189				} else {
190					errx(EXIT_FAILURE,
191					    "Illegal mode number: %s", cps[0]);
192				}
193
194			if ((gv->pixel_clock == 0) ||
195			    (gv->disp_width == 0) ||
196			    (gv->disp_height == 0) ||
197			    (gv->depth == 0) ||
198			    (gv->hblank_start == 0) ||
199			    (gv->hsync_start == 0) ||
200			    (gv->hsync_stop == 0) ||
201			    (gv->htotal == 0) ||
202			    (gv->vblank_start == 0) ||
203			    (gv->vsync_start == 0) ||
204			    (gv->vsync_stop == 0) ||
205			    (gv->vtotal == 0)) {
206				errx(EXIT_FAILURE, "Illegal value in "
207				    "mode #%d: `%s'", gv->mode_num, obuf);
208			}
209
210			if (strstr(obuf, "default") != NULL) {
211				gv->disp_flags = GRF_FLAGS_DEFAULT;
212			} else {
213				gv->disp_flags = GRF_FLAGS_DEFAULT;
214				for (grf_flagp = grf_flags;
215				  grf_flagp->grf_flag_number; grf_flagp++) {
216				    if (strstr(obuf, grf_flagp->grf_flag_name) != NULL) {
217					gv->disp_flags |= grf_flagp->grf_flag_number;
218				    }
219				}
220				if (gv->disp_flags == GRF_FLAGS_DEFAULT)
221					errx(EXIT_FAILURE, "Your are using a "
222					    "mode file with an obsolete "
223					    "format");
224			}
225
226			/*
227			 * Check for impossible gv->disp_flags:
228			 * doublescan and interlace,
229			 * +hsync and -hsync
230			 * +vsync and -vsync.
231			 */
232			errortext = NULL;
233			if ((gv->disp_flags & GRF_FLAGS_DBLSCAN) &&
234			    (gv->disp_flags & GRF_FLAGS_LACE))
235				errortext = "Interlace and Doublescan";
236			if ((gv->disp_flags & GRF_FLAGS_PHSYNC) &&
237			    (gv->disp_flags & GRF_FLAGS_NHSYNC))
238				errortext = "+hsync and -hsync";
239			if ((gv->disp_flags & GRF_FLAGS_PVSYNC) &&
240			    (gv->disp_flags & GRF_FLAGS_NVSYNC))
241				errortext = "+vsync and -vsync";
242
243			if (errortext != NULL)
244				errx(EXIT_FAILURE, "Illegal flags in "
245				    "mode #%d: `%s' are both defined",
246				    gv->mode_num, errortext);
247
248			/* Check for old horizontal cycle values */
249			if ((gv->htotal < (gv->disp_width / 4))) {
250				gv->hblank_start *= 8;
251				gv->hsync_start *= 8;
252				gv->hsync_stop *= 8;
253				gv->htotal *= 8;
254				suggest(gv, "horizontal videoclock cycle "
255				    "values", obuf);
256				return EXIT_FAILURE;
257			}
258
259			/* Check for old interlace or doublescan modes */
260			uplim = gv->disp_height + (gv->disp_height / 4);
261			lowlim = gv->disp_height - (gv->disp_height / 4);
262			if (((gv->vtotal * 2) > lowlim) &&
263			    ((gv->vtotal * 2) < uplim)) {
264				gv->vblank_start *= 2;
265				gv->vsync_start *= 2;
266				gv->vsync_stop *= 2;
267				gv->vtotal *= 2;
268				gv->disp_flags &= ~GRF_FLAGS_DBLSCAN;
269				gv->disp_flags |= GRF_FLAGS_LACE;
270				suggest(gv, "vertical values for interlace "
271				    "modes", obuf);
272				return EXIT_FAILURE;
273			} else if (((gv->vtotal / 2) > lowlim) &&
274			    ((gv->vtotal / 2) < uplim)) {
275				gv->vblank_start /= 2;
276				gv->vsync_start /= 2;
277				gv->vsync_stop /= 2;
278				gv->vtotal /= 2;
279				gv->disp_flags &= ~GRF_FLAGS_LACE;
280				gv->disp_flags |= GRF_FLAGS_DBLSCAN;
281				suggest(gv, "vertical values for doublescan "
282				    "modes", obuf);
283				return EXIT_FAILURE;
284			}
285
286			if (testmode == 1) {
287				if (lineno == 1)
288					printf("num clk wid hi dep hbs "
289					    "hss hse ht vbs vss vse vt "
290					    "flags\n");
291				print_modeline(stdout, gv, 1);
292			} else {
293				gv->mode_descr[0] = 0;
294				if (ioctl(grffd, GRFIOCSETMON, (char *) gv) < 0)
295					err(EXIT_FAILURE, "bad monitor "
296					    "definition for mode #%d",
297					    gv->mode_num);
298			}
299		}
300		fclose(fp);
301	} else {
302		ioctl(grffd, GRFGETNUMVM, &y);
303		y += 2;
304		for (c = 1; c < y; c++) {
305			c = gv->mode_num = (c != (y - 1)) ? c : 255;
306			if (ioctl(grffd, GRFGETVMODE, gv) < 0)
307				continue;
308			if (rawdata) {
309				print_modeline(stdout, gv, 0);
310				continue;
311			}
312			if (c == 255)
313				printf("Console: ");
314			else
315				printf("%2d: ", gv->mode_num);
316
317			printf("%dx%d",
318			    gv->disp_width,
319			    gv->disp_height);
320
321			if (c != 255)
322				printf("x%d", gv->depth);
323			else
324				printf(" (%dx%d)",
325				    gv->disp_width / 8,
326				    gv->disp_height / gv->depth);
327
328			printf("\t%ld.%ldkHz @ %ldHz",
329			    gv->pixel_clock / (gv->htotal * 1000),
330			    (gv->pixel_clock / (gv->htotal * 100))
331    	    	    	    	% 10,
332			    gv->pixel_clock / (gv->htotal * gv->vtotal));
333			printf(" flags:");
334
335			if (gv->disp_flags == GRF_FLAGS_DEFAULT) {
336				printf(" default\n");
337				continue;
338			}
339
340			for (grf_flagp = grf_flags;
341			    grf_flagp->grf_flag_number; grf_flagp++)
342				if (gv->disp_flags & grf_flagp->grf_flag_number)
343					printf(" %s", grf_flagp->grf_flag_name);
344			printf("\n");
345		}
346	}
347
348	close(grffd);
349	return EXIT_SUCCESS;
350}
351
352static void
353suggest(struct	grfvideo_mode *gv, const char *d, const char *s)
354{
355	warnx("Old and no longer supported %s: %s", d, s);
356	warnx("Wrong mode line, this could be a possible good model line:");
357	fprintf(stderr, "%s: ", getprogname());
358	print_modeline(stderr, gv, 0);
359}
360
361static void
362print_modeline(FILE *fp, struct grfvideo_mode *gv, int rawflags)
363{
364	struct	grf_flag *grf_flagp;
365
366	if (gv->mode_num == 255)
367		fprintf(fp, "c ");
368	else
369		fprintf(fp, "%d ", gv->mode_num);
370
371	fprintf(fp, "%ld %d %d %d %d %d %d %d %d %d %d %d",
372		gv->pixel_clock,
373		gv->disp_width,
374		gv->disp_height,
375		gv->depth,
376		gv->hblank_start,
377		gv->hsync_start,
378		gv->hsync_stop,
379		gv->htotal,
380		gv->vblank_start,
381		gv->vsync_start,
382		gv->vsync_stop,
383		gv->vtotal);
384
385	if (rawflags) {
386		fprintf(fp, " 0x%.2x\n", gv->disp_flags);
387		return;
388	}
389	if (gv->disp_flags == GRF_FLAGS_DEFAULT) {
390		fprintf(fp, " default\n");
391		return;
392	}
393
394	for (grf_flagp = grf_flags; grf_flagp->grf_flag_number; grf_flagp++)
395		if (gv->disp_flags & grf_flagp->grf_flag_number)
396			fprintf(fp, " %s", grf_flagp->grf_flag_name);
397	fprintf(fp, "\n");
398}
399