1/*
2 * Copyright 2003 by David H. Dawes.
3 * Copyright 2003 by X-Oz Technologies.
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Except as contained in this notice, the name of the copyright holder(s)
25 * and author(s) shall not be used in advertising or otherwise to promote
26 * the sale, use or other dealings in this Software without prior written
27 * authorization from the copyright holder(s) and author(s).
28 *
29 * Author: David Dawes <dawes@XFree86.Org>.
30 */
31
32#ifdef HAVE_XORG_CONFIG_H
33#include <xorg-config.h>
34#endif
35
36#include "xf86.h"
37#include "xf86Parser.h"
38#include "xf86tokens.h"
39#include "xf86Config.h"
40#include "xf86Priv.h"
41#include "xf86_OSlib.h"
42#include "xf86pciBus.h"
43#ifdef __sparc__
44# include "xf86sbusBus.h"
45#endif
46
47#ifdef sun
48# include <sys/visual_io.h>
49# include <ctype.h>
50#endif
51
52#ifdef __NetBSD__
53#if defined(__sparc__) || defined(__sparc64__)
54#include <dev/sun/fbio.h>
55extern struct sbus_devtable sbusDeviceTable[];
56#endif /* sparc / sparc64 */
57#endif /* NetBSD */
58
59/* Sections for the default built-in configuration. */
60
61#define BUILTIN_DEVICE_NAME \
62	"\"Builtin Default %s Device %d\""
63
64#define BUILTIN_DEVICE_SECTION_PRE \
65	"Section \"Device\"\n" \
66	"\tIdentifier\t" BUILTIN_DEVICE_NAME "\n" \
67	"\tDriver\t\"%s\"\n"
68
69#define BUILTIN_DEVICE_SECTION_POST \
70	"EndSection\n\n"
71
72#define BUILTIN_DEVICE_SECTION \
73	BUILTIN_DEVICE_SECTION_PRE \
74	BUILTIN_DEVICE_SECTION_POST
75
76#define BUILTIN_SCREEN_NAME \
77	"\"Builtin Default %s Screen %d\""
78
79#define BUILTIN_SCREEN_SECTION \
80	"Section \"Screen\"\n" \
81	"\tIdentifier\t" BUILTIN_SCREEN_NAME "\n" \
82	"\tDevice\t" BUILTIN_DEVICE_NAME "\n" \
83	"EndSection\n\n"
84
85#define BUILTIN_LAYOUT_SECTION_PRE \
86	"Section \"ServerLayout\"\n" \
87	"\tIdentifier\t\"Builtin Default Layout\"\n"
88
89#define BUILTIN_LAYOUT_SCREEN_LINE \
90	"\tScreen\t" BUILTIN_SCREEN_NAME "\n"
91
92#define BUILTIN_LAYOUT_SECTION_POST \
93	"EndSection\n\n"
94
95static const char **builtinConfig = NULL;
96static int builtinLines = 0;
97
98static void listPossibleVideoDrivers(char *matches[], int nmatches);
99
100/*
101 * A built-in config file is stored as an array of strings, with each string
102 * representing a single line.  AppendToConfig() breaks up the string "s"
103 * into lines, and appends those lines it to builtinConfig.
104 */
105
106static void
107AppendToList(const char *s, const char ***list, int *lines)
108{
109    char *str, *newstr, *p;
110
111    str = xnfstrdup(s);
112    for (p = strtok(str, "\n"); p; p = strtok(NULL, "\n")) {
113	(*lines)++;
114	*list = xnfrealloc(*list, (*lines + 1) * sizeof(**list));
115	newstr = xnfalloc(strlen(p) + 2);
116	strcpy(newstr, p);
117	strcat(newstr, "\n");
118	(*list)[*lines - 1] = newstr;
119	(*list)[*lines] = NULL;
120    }
121    free(str);
122}
123
124static void
125FreeList(const char ***list, int *lines)
126{
127    int i;
128
129    for (i = 0; i < *lines; i++) {
130	free((char *)((*list)[i]));
131    }
132    free(*list);
133    *list = NULL;
134    *lines = 0;
135}
136
137static void
138FreeConfig(void)
139{
140    FreeList(&builtinConfig, &builtinLines);
141}
142
143static void
144AppendToConfig(const char *s)
145{
146    AppendToList(s, &builtinConfig, &builtinLines);
147}
148
149Bool
150xf86AutoConfig(void)
151{
152    char *deviceList[20];
153    char **p;
154    const char **cp;
155    char buf[1024];
156    ConfigStatus ret;
157
158    listPossibleVideoDrivers(deviceList, 20);
159
160    for (p = deviceList; *p; p++) {
161	snprintf(buf, sizeof(buf), BUILTIN_DEVICE_SECTION, *p, 0, *p);
162	AppendToConfig(buf);
163	snprintf(buf, sizeof(buf), BUILTIN_SCREEN_SECTION, *p, 0, *p, 0);
164	AppendToConfig(buf);
165    }
166
167    AppendToConfig(BUILTIN_LAYOUT_SECTION_PRE);
168    for (p = deviceList; *p; p++) {
169	snprintf(buf, sizeof(buf), BUILTIN_LAYOUT_SCREEN_LINE, *p, 0);
170	AppendToConfig(buf);
171    }
172    AppendToConfig(BUILTIN_LAYOUT_SECTION_POST);
173
174    for (p = deviceList; *p; p++) {
175	free(*p);
176    }
177
178    xf86MsgVerb(X_DEFAULT, 0,
179		"Using default built-in configuration (%d lines)\n",
180		builtinLines);
181
182    xf86MsgVerb(X_DEFAULT, 3, "--- Start of built-in configuration ---\n");
183    for (cp = builtinConfig; *cp; cp++)
184	xf86ErrorFVerb(3, "\t%s", *cp);
185    xf86MsgVerb(X_DEFAULT, 3, "--- End of built-in configuration ---\n");
186
187    xf86initConfigFiles();
188    xf86setBuiltinConfig(builtinConfig);
189    ret = xf86HandleConfigFile(TRUE);
190    FreeConfig();
191
192    if (ret != CONFIG_OK)
193	xf86Msg(X_ERROR, "Error parsing the built-in default configuration.\n");
194
195    return ret == CONFIG_OK;
196}
197
198static void
199listPossibleVideoDrivers(char *matches[], int nmatches)
200{
201    int i;
202
203    for (i = 0 ; i < nmatches ; i++) {
204        matches[i] = NULL;
205    }
206    i = 0;
207
208#ifdef __NetBSD__
209#if defined(__shark)
210    matches[i++] = xnfstrdup("chips");
211    matches[i++] = xnfstrdup("igs");
212#elif defined(__sgimips)
213    matches[i++] = xnfstrdup("crime");
214    matches[i++] = xnfstrdup("newport");
215#elif defined(__sparc) || defined(__sparc64)
216    /* dig through /dev/fb* */
217    {
218    	struct fbtype fbt;
219	int j = 0, fd = 0, dev;
220	char fbpath[32];
221
222	for (j = 0; j < 10; j++) {
223	    snprintf(fbpath, 31, "/dev/fb%d", j);
224	    xf86Msg(X_ERROR,"%s: trying %s\n", __func__, fbpath);
225	    fd = open(fbpath, O_RDONLY, 0);
226	    if (fd == -1) continue;
227	    memset(&fbt, 0, sizeof(fbt));
228	    if (ioctl(fd, FBIOGTYPE, &fbt) == -1) {
229	    	close(fd);
230		continue;
231	    }
232	    close(fd);
233	    dev = 0;
234	    while ((sbusDeviceTable[dev].fbType != 0) &&
235	           (sbusDeviceTable[dev].fbType != fbt.fb_type))
236		dev++;
237	    if (sbusDeviceTable[dev].fbType == fbt.fb_type) {
238		xf86Msg(X_ERROR,"%s: found %s\n", __func__,
239		    sbusDeviceTable[dev].driverName);
240		matches[i++] = xnfstrdup(sbusDeviceTable[dev].driverName);
241	    }
242	}
243    }
244#endif
245
246#else /* !NetBSD */
247#ifdef sun
248    /* Check for driver type based on /dev/fb type and if valid, use
249       it instead of PCI bus probe results */
250    if (xf86Info.consoleFd >= 0) {
251	struct vis_identifier   visid;
252	const char *cp;
253	extern char xf86SolarisFbDev[PATH_MAX];
254	int iret;
255
256	SYSCALL(iret = ioctl(xf86Info.consoleFd, VIS_GETIDENTIFIER, &visid));
257	if (iret < 0) {
258	    int fbfd;
259
260	    fbfd = open(xf86SolarisFbDev, O_RDONLY);
261	    if (fbfd >= 0) {
262		SYSCALL(iret = ioctl(fbfd, VIS_GETIDENTIFIER, &visid));
263		close(fbfd);
264	    }
265	}
266
267	if (iret < 0) {
268	    xf86Msg(X_WARNING,
269		    "could not get frame buffer identifier from %s\n",
270		    xf86SolarisFbDev);
271	} else {
272	    xf86Msg(X_PROBED, "console driver: %s\n", visid.name);
273
274	    /* Special case from before the general case was set */
275	    if (strcmp(visid.name, "NVDAnvda") == 0) {
276		matches[i++] = xnfstrdup("nvidia");
277	    }
278
279	    /* General case - split into vendor name (initial all-caps
280	       prefix) & driver name (rest of the string). */
281	    if (strcmp(visid.name, "SUNWtext") != 0) {
282		for (cp = visid.name; (*cp != '\0') && isupper(*cp); cp++) {
283		    /* find end of all uppercase vendor section */
284		}
285		if ((cp != visid.name) && (*cp != '\0')) {
286		    char *driverName = xnfstrdup(cp);
287		    char *vendorName = xnfstrdup(visid.name);
288		    vendorName[cp - visid.name] = '\0';
289
290		    matches[i++] = vendorName;
291		    matches[i++] = driverName;
292		}
293	    }
294	}
295    }
296#endif
297#ifdef __sparc__
298    {
299	char *sbusDriver = sparcDriverName();
300	if (sbusDriver)
301	    matches[i++] = xnfstrdup(sbusDriver);
302    }
303#endif
304#endif /* NetBSD */
305
306    i = xf86PciMatchDriver(matches, nmatches);
307
308    /*
309     * Fallback to platform default frame buffer driver  if we didn't probe
310     * anything useful
311     */
312    if (i < (nmatches - 1)) {
313#ifdef __NetBSD__
314#if defined(__i386__) || defined(__amd64__)
315	matches[i++] = xnfstrdup("vesa");
316#endif
317#else /* !NetBSD */
318#if !defined(__linux__) && defined(__sparc__)
319	matches[i++] = xnfstrdup("wsfb");
320#else
321	matches[i++] = xnfstrdup("fbdev");
322#endif
323#if defined(__i386__) || defined(__amd64__) || defined(__hurd__)
324	matches[i++] = xnfstrdup("vesa");
325#elif defined(__sparc__) && !defined(sun)
326	matches[i++] = xnfstrdup("sunffb");
327#endif
328#endif /* NetBSD */
329    }
330
331#ifdef __NetBSD__
332    /*
333     * If we haven't found any suitable drivers, try to use wsfb.
334     */
335    if (i == 0) {
336	matches[i++] = xnfstrdup("wsfb");
337    }
338#endif
339}
340
341/* copy a screen section and enter the desired driver
342 * and insert it at i in the list of screens */
343static Bool
344copyScreen(confScreenPtr oscreen, GDevPtr odev, int i, char *driver)
345{
346    confScreenPtr nscreen;
347    GDevPtr cptr = NULL;
348
349    nscreen = malloc(sizeof(confScreenRec));
350    if (!nscreen)
351        return FALSE;
352    memcpy(nscreen, oscreen, sizeof(confScreenRec));
353
354    cptr = malloc(sizeof(GDevRec));
355    if (!cptr) {
356        free(nscreen);
357        return FALSE;
358    }
359    memcpy(cptr, odev, sizeof(GDevRec));
360
361    if (asprintf(&cptr->identifier, "Autoconfigured Video Device %s", driver)
362        == -1) {
363        free(cptr);
364        free(nscreen);
365        return FALSE;
366    }
367    cptr->driver = driver;
368
369    xf86ConfigLayout.screens[i].screen = nscreen;
370
371    /* now associate the new driver entry with the new screen entry */
372    xf86ConfigLayout.screens[i].screen->device = cptr;
373    cptr->myScreenSection = xf86ConfigLayout.screens[i].screen;
374
375    return TRUE;
376}
377
378GDevPtr
379autoConfigDevice(GDevPtr preconf_device)
380{
381    GDevPtr ptr = NULL;
382    char *matches[20]; /* If we have more than 20 drivers we're in trouble */
383    int num_matches = 0, num_screens = 0, i;
384    screenLayoutPtr slp;
385
386    if (!xf86configptr) {
387        return NULL;
388    }
389
390    /* If there's a configured section with no driver chosen, use it */
391    if (preconf_device) {
392        ptr = preconf_device;
393    } else {
394        ptr = calloc(1, sizeof(GDevRec));
395        if (!ptr) {
396            return NULL;
397        }
398        ptr->chipID = -1;
399        ptr->chipRev = -1;
400        ptr->irq = -1;
401
402        ptr->active = TRUE;
403        ptr->claimed = FALSE;
404        ptr->identifier = "Autoconfigured Video Device";
405        ptr->driver = NULL;
406    }
407    if (!ptr->driver) {
408        /* get all possible video drivers and count them */
409        listPossibleVideoDrivers(matches, 20);
410        for (; matches[num_matches]; num_matches++) {
411            xf86Msg(X_DEFAULT, "Matched %s as autoconfigured driver %d\n",
412                    matches[num_matches], num_matches);
413        }
414
415        slp = xf86ConfigLayout.screens;
416        if (slp) {
417            /* count the number of screens and make space for
418             * a new screen for each additional possible driver
419             * minus one for the already existing first one
420             * plus one for the terminating NULL */
421            for (; slp[num_screens].screen; num_screens++);
422            xf86ConfigLayout.screens = xnfcalloc(num_screens + num_matches,
423                                                sizeof(screenLayoutRec));
424            xf86ConfigLayout.screens[0] = slp[0];
425
426            /* do the first match and set that for the original first screen */
427            ptr->driver = matches[0];
428            if (!xf86ConfigLayout.screens[0].screen->device) {
429                xf86ConfigLayout.screens[0].screen->device = ptr;
430                ptr->myScreenSection = xf86ConfigLayout.screens[0].screen;
431            }
432
433            /* for each other driver found, copy the first screen, insert it
434             * into the list of screens and set the driver */
435            i = 0;
436            while (i++ < num_matches) {
437                if (!copyScreen(slp[0].screen, ptr, i, matches[i]))
438                    return NULL;
439            }
440
441            /* shift the rest of the original screen list
442             * to the end of the current screen list
443             *
444             * TODO Handle rest of multiple screen sections */
445            for (i = 1; i < num_screens; i++) {
446                xf86ConfigLayout.screens[i+num_matches] = slp[i];
447            }
448            xf86ConfigLayout.screens[num_screens+num_matches-1].screen = NULL;
449            free(slp);
450        } else {
451            /* layout does not have any screens, not much to do */
452            ptr->driver = matches[0];
453            for (i = 1; matches[i] ; i++) {
454                if (matches[i] != matches[0]) {
455                    free(matches[i]);
456                }
457            }
458        }
459    }
460
461    xf86Msg(X_DEFAULT, "Assigned the driver to the xf86ConfigLayout\n");
462
463    return ptr;
464}
465