1117N/A/*
1117N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
1117N/A *
1117N/A * Permission is hereby granted, free of charge, to any person obtaining a
1117N/A * copy of this software and associated documentation files (the "Software"),
1117N/A * to deal in the Software without restriction, including without limitation
1117N/A * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1117N/A * and/or sell copies of the Software, and to permit persons to whom the
1117N/A * Software is furnished to do so, subject to the following conditions:
1117N/A *
1117N/A * The above copyright notice and this permission notice (including the next
1117N/A * paragraph) shall be included in all copies or substantial portions of the
1117N/A * Software.
1117N/A *
1117N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1117N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1117N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1117N/A * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1117N/A * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1117N/A * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1117N/A * DEALINGS IN THE SOFTWARE.
1117N/A */
1117N/A
1117N/A
1117N/A
1117N/A#include <sys/param.h> /* MAXPATHLEN */
1117N/A#include <sys/types.h>
1117N/A#include <libscf.h> /* Service Configuration Facility */
1117N/A#include <stdarg.h> /* va_end(), va_start(), vfprintf() */
1117N/A#include <stdio.h> /* fprintf(), printf() */
1117N/A#include <stdlib.h> /* exit(), free(), malloc() */
1117N/A#include <string.h> /* strerror(), str[l]cpy(), strstr(), ... */
1117N/A#include <unistd.h> /* close(), ioctl() */
1117N/A#include <dirent.h> /* opendir(), readdir(), closedir() */
1117N/A#include <sys/stat.h> /* stat() */
1117N/A#include <sys/visual_io.h> /* VISUAL environment device identifier */
1117N/A#include <fcntl.h> /* open() */
1117N/A#include <nl_types.h>
1117N/A#include <errno.h> /* errno */
1117N/A
1117N/A#include "gfx_common.h" /* Frame buffer model name */
1117N/A
1117N/A
1117N/A#define _DEBUG_FB /* _DEBUG_FB is merely DEBUG_FB disabled */
1117N/A
1117N/A
1117N/A/*
1117N/A * Frame buffer device file names (/dev/fb[0]?, /dev/fbs/[a-z]fb[0-9][ab]?)
1117N/A */
1117N/A#define DEVICE_SYMLINK_DIR "/dev" /* Device symlink directory */
1117N/A#define DEVICE_SYMLINK_NAME "fb" /* Device symlink name */
1117N/A#define DEVICE_SYMLINK_NAME_0 DEVICE_SYMLINK_NAME "0"
1117N/A#define DEVICE_SYMLINK_PATH DEVICE_SYMLINK_DIR "/" DEVICE_SYMLINK_NAME
1117N/A#define DEVICE_SYMLINK_PATH_0 DEVICE_SYMLINK_DIR "/" DEVICE_SYMLINK_NAME_0
1117N/A
1117N/A#define DEVICE_DIR "/dev/fbs" /* Graphics device directory */
1117N/A
1117N/A#define MAX_DEV_PATH_LEN 128 /* Max device path length */
1117N/A
1117N/A#define STREAM_SUFFIX_CHARS "ab" /* Dev stream suffix chars */
1117N/A#define MAX_SUFFIX_LIST_LEN 16 /* Ample room for " [a|b]" */
1117N/A
1117N/A/*
1117N/A * Frame buffer configuration software directory
1117N/A */
1117N/A#define FBC_LIB_DIR "/usr/lib/fbconfig" /* fbconfig software dir */
1117N/A
1117N/A/*
1117N/A * fbconfig invokes the dcmtool script (which in turn invokes java)
1117N/A */
1117N/A#define DCMTOOL_LOCATION FBC_LIB_DIR "/SUNWdcm/bin/dcmtool"
1117N/A
1117N/A
1117N/A/* globals */
1117N/Astatic nl_catd catfd; /* Message catalog fd (obsolete) */
1117N/A
1117N/A/*
1117N/A * PrintError()
1117N/A *
1117N/A * Write a variable format error message to stderr, prefixed by the
1117N/A * program name.
1117N/A */
1117N/A
1117N/Astatic
1117N/Avoid
1117N/APrintError(const char *format, ...)
1117N/A{
1117N/A va_list ap; /* Variable argument pointer */
1117N/A
1117N/A va_start(ap, format);
1117N/A fprintf(stderr, "fbconfig: ");
1117N/A vfprintf(stderr, format, ap);
1117N/A fprintf(stderr, "\n");
1117N/A va_end(ap);
1117N/A
1117N/A} /* PrintError() */
1117N/A
1117N/A
1117N/A/*
1117N/A * IdentifyXServer()
1117N/A *
1117N/A * Determine what X server is currently configured (Xsun, Xorg, ...).
1117N/A *
1117N/A * Note that this is not necessarily the X server that is currently
1117N/A * running.
1117N/A *
1117N/A * Related svccfg(1M) "set" and "list" commands:
1117N/A *
1117N/A * svccfg -s svc:/application/x11/x11-server \
1117N/A * setprop options/server=/usr/openwin/bin/Xsun
1117N/A * svccfg -s svc:/application/x11/x11-server \
1208N/A * setprop options/server=/usr/bin/Xorg
1117N/A *
1117N/A * svccfg -s svc:/application/x11/x11-server listprop 'options/server'
1117N/A */
1117N/A
1117N/A#define XSRV_SERVICE_NAME "application/x11/x11-server"
1117N/A#define XSRV_PROP_GROUP_NAME "options"
1117N/A#define XSRV_PROP_NAME "server"
1117N/A
1117N/Atypedef enum {
1117N/A XSERVER_UNKNOWN = -1, /* No idea */
1117N/A XSERVER_XSUN, /* Xsun server */
1117N/A XSERVER_XORG /* Xorg server */
1117N/A} xserv_t;
1117N/A
1117N/A#define GFX_DEV_M64 0x0001
1117N/A#define GFX_DEV_FFB 0x0002
1117N/A#define GFX_DEV_AFB 0x0004
1117N/A#define GFX_DEV_IFB 0x0008
1117N/A#define GFX_DEV_JFB 0x0010
1117N/A#define GFX_DEV_PFB 0x0100
1117N/A#define GFX_DEV_NFB 0x0200
1117N/A#define GFX_DEV_EFB 0x0400
1117N/A#define GFX_DEV_KFB 0x0800
1117N/A#define GFX_DEV_AST 0x1000
1117N/A
1117N/A#define GFX_DEV_XSUN GFX_DEV_M64 | GFX_DEV_FFB | GFX_DEV_AFB | GFX_DEV_IFB | \
1117N/A GFX_DEV_JFB | GFX_DEV_PFB | GFX_DEV_NFB | GFX_DEV_EFB | GFX_DEV_KFB
1117N/A
1117N/A#if OSVER==510
1117N/A#define GFX_DEV_XORG GFX_DEV_PFB | GFX_DEV_NFB | GFX_DEV_EFB | GFX_DEV_AST
1117N/A#else
1117N/A#define GFX_DEV_XORG GFX_DEV_PFB | GFX_DEV_NFB | GFX_DEV_EFB | GFX_DEV_KFB | GFX_DEV_AST
1117N/A#endif
1117N/A
1208N/Aconst char *xserver_str[] = {"Xsun", "Xorg"};
1208N/Aconst char *xserver_path[] = {"/usr/openwin/bin/Xsun", "/usr/bin/Xorg"};
1117N/Aunsigned int xserver_device[] = { GFX_DEV_XSUN, GFX_DEV_XORG };
1117N/A
1117N/Astatic
1117N/Axserv_t
1117N/AIdentifyXServer(void)
1117N/A{
1117N/A const char *filename; /* Ptr to X server simple filename */
1117N/A char pathname[MAXPATHLEN]; /* X server pathname */
1117N/A scf_property_t *prop; /* SCF property */
1117N/A scf_propertygroup_t *prop_group; /* SCF property group */
1117N/A scf_handle_t *scf_handle; /* SCF handle */
1117N/A scf_scope_t *scope; /* SCF scope */
1117N/A scf_service_t *service; /* SCF service */
1117N/A scf_value_t *value; /* SCF property value */
1117N/A xserv_t x_server; /* Returned X server identity */
1117N/A
1117N/A /*
1117N/A * X, the unknown
1117N/A */
1117N/A x_server = XSERVER_UNKNOWN;
1117N/A
1117N/A /*
1117N/A * Allocate and initialize the necessary SCF data structures
1117N/A */
1117N/A scf_handle = scf_handle_create(SCF_VERSION);
1117N/A scope = scf_scope_create(scf_handle);
1117N/A service = scf_service_create(scf_handle);
1117N/A prop_group = scf_pg_create(scf_handle);
1117N/A prop = scf_property_create(scf_handle);
1117N/A value = scf_value_create(scf_handle);
1117N/A if ((value == NULL) ||
1117N/A (prop == NULL) ||
1117N/A (prop_group == NULL) ||
1117N/A (service == NULL) ||
1117N/A (scope == NULL) ||
1117N/A (scf_handle == NULL)) {
1117N/A#ifdef DEBUG_FB
1117N/A PrintError("SCF resource creation, %s",
1117N/A scf_strerror(scf_error()));
1117N/A#endif
1117N/A goto clean_up;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Retrieve the Nul-terminated X server pathname string
1117N/A */
1117N/A if ((scf_handle_bind(scf_handle) == -1) ||
1117N/A (scf_handle_get_scope(scf_handle, SCF_SCOPE_LOCAL, scope) == -1) ||
1117N/A (scf_scope_get_service(scope, XSRV_SERVICE_NAME, service) == -1) ||
1117N/A (scf_service_get_pg(service, XSRV_PROP_GROUP_NAME, prop_group)
1117N/A == -1) ||
1117N/A (scf_pg_get_property(prop_group, XSRV_PROP_NAME, prop) == -1) ||
1117N/A (scf_property_get_value(prop, value) == -1) ||
1117N/A (scf_value_get_astring(value, pathname, sizeof(pathname)) == -1)) {
1117N/A#ifdef DEBUG_FB
1117N/A PrintError("SCF value retrieval, %s",
1117N/A scf_strerror(scf_error()));
1117N/A#endif
1117N/A goto clean_up;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Evaluate the simple filename
1117N/A */
1117N/A filename = strrchr(pathname, '/');
1117N/A if (filename == NULL) {
1117N/A filename = pathname;
1117N/A } else {
1117N/A filename += 1;
1117N/A }
1117N/A if (strcmp(filename, "Xsun") == 0) {
1117N/A x_server = XSERVER_XSUN;
1117N/A } else
1117N/A if (strcmp(filename, "Xorg") == 0) {
1117N/A x_server = XSERVER_XORG;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Destroy our resources
1117N/A */
1117N/Aclean_up:
1117N/A if (value != NULL) {
1117N/A scf_value_destroy(value);
1117N/A }
1117N/A if (prop != NULL) {
1117N/A scf_property_destroy(prop);
1117N/A }
1117N/A if (prop_group != NULL) {
1117N/A scf_pg_destroy(prop_group);
1117N/A }
1117N/A if (service != NULL) {
1117N/A scf_service_destroy(service);
1117N/A }
1117N/A if (scope != NULL) {
1117N/A scf_scope_destroy(scope);
1117N/A }
1117N/A if (scf_handle != NULL) {
1117N/A scf_handle_destroy(scf_handle);
1117N/A }
1117N/A
1117N/A /*
1117N/A * Return the identity of the X server
1117N/A */
1117N/A return (x_server);
1117N/A
1117N/A} /* IdentifyXServer() */
1117N/A
1117N/A
1117N/A/*
1117N/A * CallConfigProgram()
1117N/A *
1117N/A * Become the actual device configuration program, executing it with
1117N/A * the caller-provided argument vector. This function does not
1117N/A * return.
1117N/A */
1117N/A
1117N/Astatic
1117N/Avoid
1117N/ACallConfigProgram(char **argv)
1117N/A{
1117N/A#ifdef DEBUG_FB
1117N/A if (argv == NULL) {
1117N/A printf("CallConfigProgram(argv is NULL)\n");
1117N/A exit(1);
1117N/A }
1117N/A {
1117N/A int i;
1117N/A
1117N/A for (i = 0; argv[i] != NULL; i++) {
1117N/A printf("argv[%d]=%s\n", i, argv[i]);
1117N/A }
1117N/A }
1117N/A#endif
1117N/A
1117N/A if (argv[0] == NULL) {
1117N/A PrintError(catgets(catfd, 1, 1,
1117N/A "No configuration program pathname"));
1117N/A } else {
1117N/A (void) execvp(argv[0], argv);
1117N/A PrintError("Cannot run config program, %s", strerror(errno));
1117N/A }
1117N/A
1117N/A exit(1);
1117N/A
1117N/A} /* CallConfigProgram() */
1117N/A
1117N/A
1117N/A/*
1117N/A * get_device_model()
1117N/A *
1117N/A * Return the frame buffer model name, else an empty string, in the
1117N/A * caller-supplied device_model[GFX_MAX_MODELNAME_LEN] name buffer.
1117N/A *
1117N/A * Note that ioctl(GFX_IOCTL_GET_IDENTIFIER) might not be supported
1117N/A * by older drivers (e.g., Xsun drivers).
1117N/A */
1117N/A
1117N/Astatic
1117N/Avoid
1117N/Aget_device_model(
1117N/A int device_fd, /* Device file descriptor number */
1117N/A char *device_model) /* Returned device model name */
1117N/A{
1117N/A const char *const SUNW_ = "SUNW,"; /* Prefix on model & part strings */
1117N/A struct gfx_identifier gfx_ident; /* Graphics identifier */
1117N/A const char *model_name; /* Frame buffer model name */
1117N/A
1117N/A /*
1117N/A * Get the frame buffer model name w/o any leading "SUNW," substring
1117N/A */
1117N/A model_name = "";
1117N/A if (ioctl(device_fd, GFX_IOCTL_GET_IDENTIFIER, &gfx_ident) == 0) {
1117N/A if (gfx_ident.flags & GFX_IDENT_MODELNAME) {
1117N/A model_name = &gfx_ident.model_name[0];
1117N/A if (strstr(model_name, SUNW_) != NULL) {
1117N/A model_name += strlen(SUNW_);
1117N/A }
1117N/A }
1117N/A }
1117N/A
1117N/A /*
1117N/A * Return the frame buffer model name, else an empty string
1117N/A */
1117N/A (void) strlcpy(device_model, model_name, GFX_MAX_MODELNAME_LEN);
1117N/A
1117N/A} /* get_device_model() */
1117N/A
1117N/A
1117N/A/*
1117N/A * get_device_identification()
1117N/A *
1117N/A * Given a /dev/fb or /dev/fbs/... pathname, make sure it's for a
1117N/A * character special file. If the caller has provided the necessary
1117N/A * pointer, return the (struct stat).st_rdev value. Open the file
1117N/A * and return the VISUAL environment device identifier name. For
1117N/A * a device such as "/dev/fbs/jfb0", the identifier name should be
1117N/A * "SUNWjfb". If the caller has provided the necessary
1117N/A * device_model[GFX_MAX_MODELNAME_LEN] name buffer, get and return
1117N/A * the frame buffer model name.
1117N/A *
1117N/A * In the event of an error, return an errno-style error code. An
1117N/A * EACCES code may be of special interest to the caller.
1117N/A */
1117N/A
1117N/A/*
1117N/A * Mask for stat_buf.st_rdev member
1117N/A */
1117N/A#define MINOR_MASK 0xFFFF /* Invert to get major/type bits */
1117N/A
1117N/Astatic
1117N/Aint
1117N/Aget_device_identification(
1117N/A const char *device_path, /* Device pathname */
1117N/A int *st_rdev, /* Optionally returned st_rdev value */
1117N/A struct vis_identifier *vis_ident, /* Returned VISUAL env identifier */
1117N/A char *device_model) /* Optionally returned model name */
1117N/A{
1117N/A int error_code; /* Returned errno-style error code */
1117N/A int device_fd; /* Frame buffer file descriptor # */
1117N/A struct stat stat_buf; /* stat() buffer for device file */
1117N/A
1117N/A /*
1117N/A * Make sure this is a character special file
1117N/A */
1117N/A if (stat(device_path, &stat_buf) != 0) {
1117N/A return (errno);
1117N/A }
1117N/A if ((stat_buf.st_mode & S_IFCHR) == 0) {
1117N/A return (ENODEV); /* "No such device" almost says it */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Return the st_rdev value if the caller wants it
1117N/A */
1117N/A if (st_rdev != NULL) {
1117N/A *st_rdev = stat_buf.st_rdev;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Open the existing device file
1117N/A */
1117N/A device_fd = open(device_path, O_RDONLY);
1117N/A if (device_fd == -1) {
1117N/A return (errno); /* Error code, e.g. EACCES */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Get the VISUAL environment device identifier name
1117N/A */
1117N/A error_code = 0;
1117N/A if (ioctl(device_fd, VIS_GETIDENTIFIER, vis_ident) == -1) {
1117N/A error_code = errno;
1117N/A }
1117N/A
1117N/A /*
1117N/A * If the caller wants it, return the frame buffer model name
1117N/A */
1117N/A if (device_model != NULL) {
1117N/A get_device_model(device_fd, device_model);
1117N/A }
1117N/A
1117N/A close(device_fd);
1117N/A
1117N/A return (error_code);
1117N/A
1117N/A} /* get_device_identification() */
1117N/A
1117N/A
1117N/Atypedef struct {
1117N/A char *pathname; /* Full pathname (e.g. "/dev/fb") */
1117N/A char *filename; /* Simple filename (e.g. "fb") */
1117N/A} path_file_t;
1117N/A
1117N/Astatic const path_file_t default_device[] = {
1117N/A { DEVICE_SYMLINK_PATH, DEVICE_SYMLINK_NAME },
1117N/A { DEVICE_SYMLINK_PATH_0, DEVICE_SYMLINK_NAME_0 },
1117N/A { NULL, NULL }
1117N/A};
1117N/A
1117N/A
1117N/A/*
1117N/A * GetDefaultDevicePathname()
1117N/A *
1117N/A * This function looks for a default device and returns the pathname
1117N/A * of the first default frame buffer device that is found and can be
1117N/A * opened and provide a VISUAL environment device identifier. The
1117N/A * possible return values are:
1117N/A * Zero - Success; a default device exists and can be accessed
1117N/A * ENOENT - No frame buffer device was found
1117N/A * EACCES - Something was found, but the user can't access it
1117N/A */
1117N/A
1117N/Astatic
1117N/Aint
1117N/AGetDefaultDevicePathname(
1117N/A char **device_path) /* Returned default device pathname */
1117N/A{
1117N/A const path_file_t *default_dev; /* Ptr to a default_device[] element */
1117N/A int err_code; /* get_device_identification() code */
1117N/A int error_code; /* Return EACCES, ENOENT, or zero */
1117N/A struct vis_identifier vis_ident; /* Device vis identifier */
1117N/A
1117N/A *device_path = NULL;
1117N/A error_code = ENOENT;
1117N/A
1117N/A /*
1117N/A * See whether any default device will open and identify itself
1117N/A */
1117N/A for (default_dev = &default_device[0];
1117N/A default_dev->pathname != NULL;
1117N/A default_dev += 1) {
1117N/A err_code = get_device_identification(
1117N/A default_dev->pathname, NULL, &vis_ident, NULL);
1117N/A if (err_code == 0) {
1117N/A *device_path = default_dev->pathname;
1117N/A return (0); /* At least one FB device exists */
1117N/A }
1117N/A if (err_code == EACCES) {
1117N/A error_code = EACCES; /* Caller may have to complain */
1117N/A if (*device_path == NULL) {
1117N/A *device_path = default_dev->pathname;
1117N/A }
1117N/A }
1117N/A }
1117N/A
1117N/A return (error_code);
1117N/A
1117N/A} /* GetDefaultDevicePathname() */
1117N/A
1117N/A
1117N/A/*
1117N/A * GetDevicePathname()
1117N/A *
1117N/A * Given the pathname or simple filename of a real or default frame
1117N/A * buffer device, return the fully qualified pathname.
1117N/A *
1117N/A * Partial pathnames will be treated arbitrarily in nonsense-in,
1117N/A * nonsense-out fashion.
1117N/A *
1117N/A * The caller-supplied buffer will be used if it is necessary to
1117N/A * construct a fully qualified pathname from path components.
1117N/A */
1117N/A
1117N/Astatic
1117N/Achar *
1117N/AGetDevicePathname(
1117N/A char *device_name, /* Free-form device name */
1117N/A char *device_path_buf, /* Ptr to device pathname buffer */
1117N/A size_t device_path_buflen) /* Device path buffer length */
1117N/A{
1117N/A const path_file_t *default_dev; /* Ptr to a default_device[] element */
1117N/A
1117N/A#if (0) /* Currently unused */
1117N/A /*
1117N/A * If there's no device name, return the most likely default pathname
1117N/A */
1117N/A if (device_name == NULL) {
1117N/A (void) GetDefaultDevicePathname(&device_name);
1117N/A if (device_name == NULL) {
1117N/A device_name = default_device[0].pathname;
1117N/A }
1117N/A return (device_name);
1117N/A }
1117N/A
1117N/A#endif /* Currently unused */
1117N/A /*
1117N/A * Convert a default device name to a full pathname
1117N/A */
1117N/A for (default_dev = &default_device[0];
1117N/A default_dev->pathname != NULL;
1117N/A default_dev += 1) {
1117N/A if ((strcmp(device_name, default_dev->pathname) == 0) ||
1117N/A (strcmp(device_name, default_dev->filename) == 0)) {
1117N/A return (default_dev->pathname);
1117N/A }
1117N/A }
1117N/A
1117N/A /*
1117N/A * Try to convert a real device filename to a full pathname
1117N/A */
1117N/A if (*device_name != '/') {
1117N/A const char *const device_dir = DEVICE_DIR "/";
1117N/A
1117N/A if (strlen(device_dir) + strlen(device_name)
1117N/A < device_path_buflen) {
1117N/A strcpy(device_path_buf, device_dir);
1117N/A strcat(device_path_buf, device_name);
1117N/A device_name = device_path_buf;
1117N/A }
1117N/A }
1117N/A
1117N/A /*
1117N/A * Return the full pathname or whatever
1117N/A */
1117N/A return (device_name);
1117N/A
1117N/A} /* GetDevicePathname() */
1117N/A
1117N/A
1117N/A/*
1117N/A * GetConfigProgramPath()
1117N/A *
1117N/A * Given the current X server and the identifier for the frame buffer
1117N/A * device, construct the full pathname of the relevant configuration
1117N/A * program. Return the program pathname and a sense of whether it
1117N/A * exists (ENOENT), is accessible (EACCES), etc.
1117N/A *
1117N/A * The caller is responsible for freeing the returned pathname
1117N/A * string.
1117N/A */
1117N/A
1117N/Astatic
1117N/Aint
1117N/AGetConfigProgramPath(
1117N/A xserv_t x_server, /* X server: Xsun, Xorg, ... */
1117N/A const char *vis_ident_name, /* VISUAL env device identifier */
1117N/A char **config_prog_path) /* Returned config program path */
1117N/A{
1117N/A const char *const xsun_prog_fmt = FBC_LIB_DIR "/%s_config";
1117N/A const char *const xorg_prog_fmt = FBC_LIB_DIR "/%s_conf";
1117N/A const char *const xorg_lib_fmt = FBC_LIB_DIR "/lib%s_conf.so";
1117N/A const char *const xorg_prog_path = FBC_LIB_DIR "/fbconf_xorg";
1117N/A int error_code; /* Error code (see errno.h) */
1117N/A size_t len; /* String length temp */
1117N/A size_t len_ident; /* Length of device identifier name */
1117N/A size_t len_path; /* Max length of path memory needed */
1117N/A
1117N/A *config_prog_path = NULL;
1117N/A error_code = 0;
1117N/A
1117N/A /*
1117N/A * Build server-specific pathnames of config programs
1117N/A */
1117N/A len_ident = strlen(vis_ident_name);
1117N/A
1117N/A switch (x_server) {
1117N/A
1117N/A default:
1117N/A /*
1117N/A * Commandeer EINVAL to indicate that the X server is unknown
1117N/A */
1117N/A return (EINVAL); /* Invalid x_server argument */
1117N/A
1117N/A case XSERVER_XSUN:
1117N/A *config_prog_path = malloc(strlen(xsun_prog_fmt) + len_ident);
1117N/A if (*config_prog_path == NULL) {
1117N/A return (ENOMEM);
1117N/A }
1117N/A sprintf(*config_prog_path, xsun_prog_fmt, vis_ident_name);
1117N/A break;
1117N/A
1117N/A case XSERVER_XORG:
1117N/A /*
1117N/A * Allocate memory to contain any one of the pathnames:
1117N/A * * any device-specific config program
1117N/A * * the device-specific library used by fbconf_xorg
1117N/A * * the multi-device config program, fbconf_xorg
1117N/A *
1117N/A * Note that the two-character "%s" conversion spec
1117N/A * in the sprintf() format string is replaced by the
1117N/A * vis_ident_name string. Its trifling length could
1117N/A * be ignored but we'll count it against the length
1117N/A * of the Nul terminator.
1117N/A */
1117N/A len_path = strlen(xorg_lib_fmt);
1117N/A len = strlen(xorg_prog_fmt);
1117N/A if (len_path < len) {
1117N/A len_path = len;
1117N/A }
1117N/A len_path += len_ident;
1117N/A len = strlen(xorg_prog_path) + 1;
1117N/A if (len_path < len) {
1117N/A len_path = len;
1117N/A }
1117N/A
1117N/A *config_prog_path = malloc(len_path);
1117N/A if (*config_prog_path == NULL) {
1117N/A return (ENOMEM);
1117N/A }
1117N/A
1117N/A /*
1117N/A * See if a config program exists, accessible or not
1117N/A */
1117N/A sprintf(*config_prog_path, xorg_prog_fmt, vis_ident_name);
1117N/A if (access(*config_prog_path, X_OK) == 0) {
1117N/A return (0); /* Have a device-specific program */
1117N/A }
1117N/A if (errno != ENOENT) {
1117N/A return (errno); /* Have a program w/ encumbrances */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Make a note of whether a device-specific library exists
1117N/A *
1117N/A * We'll return the multi-device config program path
1117N/A * in any case. The caller can decide, based on the
1117N/A * error code, whether and how it could be useful.
1117N/A */
1117N/A sprintf(*config_prog_path, xorg_lib_fmt, vis_ident_name);
1117N/A if (access(*config_prog_path, X_OK) != 0) {
1117N/A error_code = errno;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Construct the multi-device config program pathname
1117N/A *
1117N/A * It's assumed that the states of existence and
1117N/A * accessibility of the config program, fbconf_xorg,
1117N/A * also hold true for its common library,
1117N/A * libfbconf_xorg.so.
1117N/A */
1117N/A strcpy(*config_prog_path, xorg_prog_path);
1117N/A break;
1117N/A }
1117N/A
1117N/A /*
1117N/A * See if the config program exists and allows the user to execute it
1117N/A */
1117N/A if (access(*config_prog_path, X_OK) != 0) {
1117N/A if (error_code != ENOENT) {
1117N/A error_code = errno; /* Supersede an Xorg lib error */
1117N/A }
1117N/A }
1117N/A
1117N/A return (error_code);
1117N/A
1117N/A} /* GetConfigProgramPath() */
1117N/A
1117N/A
1117N/A/*
1117N/A * FindConfigProgram()
1117N/A *
1117N/A * Return the full pathname of the configuration program associated
1117N/A * with this X server and fully qualified device pathname.
1117N/A *
1117N/A * In the event of an error, an error code from errno.h space will be
1117N/A * returned, along with a NULL pathname pointer.
1117N/A *
1117N/A * The caller is responsible for freeing the returned pathname
1117N/A * string.
1117N/A */
1117N/Astatic
1117N/Aint
1117N/AFindConfigProgram(
1117N/A xserv_t x_server, /* X server: Xsun, Xorg, ... */
1117N/A const char *device_path, /* Device pathname */
1117N/A char **config_prog_path) /* Returned config program path */
1117N/A{
1117N/A int error_code; /* Error code (see errno.h) */
1117N/A struct vis_identifier vis_ident; /* VISUAL env device identifier */
1117N/A
1117N/A *config_prog_path = NULL;
1117N/A
1117N/A /*
1117N/A * Validate the device and get its VISUAL environment identifier
1117N/A */
1117N/A error_code = get_device_identification(
1117N/A device_path, NULL, &vis_ident, NULL);
1117N/A if (error_code != 0) {
1117N/A PrintError("%s, %s", strerror(error_code), device_path);
1117N/A if (error_code != EACCES) {
1117N/A PrintError(catgets(catfd, 12, 1,
1117N/A "Not a configurable device. "
1117N/A " Use -list to show valid devices."));
1117N/A }
1117N/A return (error_code);
1117N/A }
1117N/A
1117N/A /*
1117N/A * Get the config program to use with this X server and device type
1117N/A */
1117N/A error_code = GetConfigProgramPath(
1117N/A x_server, vis_ident.name, config_prog_path);
1117N/A if (error_code != 0) {
1117N/A PrintError("%s, %s", strerror(error_code),
1117N/A (*config_prog_path == NULL) ?
1117N/A "config program" : *config_prog_path);
1117N/A free(*config_prog_path); /* Release any memory */
1117N/A *config_prog_path = NULL;
1117N/A }
1117N/A
1117N/A return (error_code);
1117N/A
1117N/A} /* FindConfigProgram() */
1117N/A
1117N/A
1117N/A/*
1117N/A * PathToGUI()
1117N/A *
1117N/A * Return the pathname of the DCM Tool GUI script and an errno-style
1117N/A * error code indicating whether the pathname is useable.
1117N/A */
1117N/A
1117N/Astatic
1117N/Aint
1117N/APathToGUI(
1117N/A const char **gui_path) /* Returned GUI script pathname */
1117N/A{
1117N/A int error_code; /* Error code (see errno.h) */
1117N/A
1117N/A *gui_path = DCMTOOL_LOCATION;
1117N/A error_code = 0;
1117N/A if (access(*gui_path, X_OK) < 0) {
1117N/A error_code = errno;
1117N/A#ifdef DEBUG_FB
1117N/A printf("PathToGUI: %s\n", strerror(error_code));
1117N/A#endif
1117N/A }
1117N/A return (error_code);
1117N/A
1117N/A} /* PathToGUI() */
1117N/A
1117N/A
1117N/A/*
1117N/A * PrintHelp()
1117N/A *
1117N/A * Display this program's command line syntax. Include the -gui
1117N/A * option only if PathToGUI() returns a non-zero error code,
1117N/A * indicating that the returned GUI pathname is useable. Then invoke
1117N/A * the actual device configuration program, iff known, to display its
1117N/A * own -help text. (The device configuration program must implement
1117N/A * the -dev and the -help option.)
1117N/A */
1117N/A
1117N/Astatic
1117N/Avoid
1117N/APrintHelp(
1117N/A xserv_t x_server, /* X server: Xsun, Xorg, ... */
1117N/A char *device_path) /* Device pathname */
1117N/A{
1117N/A char *config_prog_path; /* Device config program pathname */
1117N/A int error_code; /* Error code from PathToGUI() */
1117N/A const char *gui_path; /* DCM Tool GUI pathname (ignored) */
1117N/A const char *gui_synopsis; /* -gui synopsis text, if any */
1117N/A
1117N/A /*
1117N/A * Decide whether to show the -gui option synopsis and description
1117N/A */
1117N/A gui_synopsis = "";
1117N/A#if (1) /* ??? Cover for temporarily absent functionality ??? */
1117N/A error_code = EINVAL;
1117N/A if (x_server == XSERVER_XSUN) {
1117N/A#endif
1117N/A error_code = PathToGUI(&gui_path);
1117N/A if (error_code == 0) {
1117N/A gui_synopsis = catgets(catfd, 13, 1, " [-gui]");
1117N/A }
1117N/A#if (1) /* ??? Cover for temporarily absent functionality ??? */
1117N/A }
1117N/A#endif
1117N/A
1117N/A /*
1117N/A * Display synopsis (usage) and description text
1117N/A */
1117N/A printf(catgets(catfd, 2, 1,
1117N/A "Usage:\n"
1117N/A "\tfbconfig [-dev devname] [-help] [-list]%s\n"
1117N/A "\t [device-specific-options]\n"
1117N/A "\t-dev\t\tSpecify the frame buffer device file name.\n"
1117N/A "\t-help\t\tDisplay this help text.\n"
1117N/A "\t-list\t\tList installed and configurable frame buffers.\n"
1117N/A "\t-xserver [Xorg | Xsun]\tConfigure the Xserver to the specified program.\n"),
1117N/A gui_synopsis);
1117N/A if (error_code == 0) {
1117N/A printf(catgets(catfd, 15, 1,
1117N/A "\t-gui\t\tInvoke Graphical User Interface (SUNWdcm)\n"
1117N/A "\t\t\tto configure devices and update Xservers file.\n"));
1117N/A }
1117N/A printf("\n"
1117N/A "\tdevice-specific-options are implemented by the device\n"
1117N/A "\t\t\tconfiguration program.\n"
1117N/A "\n");
1117N/A
1117N/A /*
1117N/A * Invoke the device config program to display its own -help text
1117N/A */
1117N/A if (device_path != NULL) {
1117N/A static char *argv[] = { NULL, "-dev", NULL, "-help", NULL };
1117N/A
1117N/A (void) FindConfigProgram(
1117N/A x_server, device_path, &config_prog_path);
1117N/A if (config_prog_path != NULL) {
1117N/A argv[0] = config_prog_path;
1117N/A argv[2] = device_path;
1117N/A CallConfigProgram(argv);
1117N/A }
1117N/A }
1117N/A
1117N/A} /* PrintHelp() */
1117N/A
1117N/A
1117N/A/*
1117N/A * ListDevices_StreamCheck()
1117N/A *
1117N/A * See whether the specified "device" name and "stream" name are in
1117N/A * fact a device file and a stream file for the same device. (It is
1117N/A * possible that the device file is actually a stream for a previous
1117N/A * device. The stream file could be any frame buffer entity.) If a
1117N/A * device/stream relationship exists, return the stream suffix
1117N/A * character. Return a Nul character otherwise.
1117N/A */
1117N/Astatic
1117N/Achar
1117N/AListDevices_StreamCheck(
1117N/A const char *device_name, /* Device file name */
1117N/A int device_rdev, /* st_rdev value returned by stat() */
1117N/A struct vis_identifier *device_vis_ident, /* Device VIS identifier */
1117N/A const char *stream_name) /* Potential stream file name */
1117N/A{
1117N/A size_t device_len; /* Length of device file name */
1117N/A size_t stream_len; /* Length of potential stream name */
1117N/A char stream_path[MAX_DEV_PATH_LEN]; /* Stream pathname */
1117N/A int stream_rdev; /* st_rdev value returned by stat() */
1117N/A struct vis_identifier stream_vis_ident; /* Stream VIS env identifier */
1117N/A
1117N/A /*
1117N/A * See if the potential stream name is the device name w/ a stream char
1117N/A */
1117N/A device_len = strlen(device_name);
1117N/A stream_len = strlen(stream_name);
1117N/A if ((stream_len != device_len + 1) ||
1117N/A (strncmp(stream_name, device_name, device_len) != 0) ||
1117N/A (strchr(STREAM_SUFFIX_CHARS, *(stream_name + device_len))
1117N/A == NULL)) {
1117N/A return ('\0'); /* The name strings are unrelated */
1117N/A }
1117N/A
1117N/A /*
1117N/A * See if the potential stream has the same device identifiers
1117N/A *
1117N/A * Note that our caller, ListDevices(), has compared
1117N/A * this pathname length to the buffer length. Oversized
1117N/A * names were rejected.
1117N/A */
1117N/A strcpy(stream_path, DEVICE_DIR "/");
1117N/A strcat(stream_path, stream_name);
1117N/A if (get_device_identification(
1117N/A stream_path, &stream_rdev, &stream_vis_ident, NULL) != 0) {
1117N/A return ('\0'); /* Decline to pursue this */
1117N/A }
1117N/A if (((stream_rdev & ~MINOR_MASK) != (device_rdev & ~MINOR_MASK)) ||
1117N/A (strcmp(stream_vis_ident.name, device_vis_ident->name) != 0)) {
1117N/A return ('\0'); /* Device identifiers don't match */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Return with the stream suffix character
1117N/A */
1117N/A return (*(stream_name + device_len));
1117N/A
1117N/A} /* ListDevices_StreamCheck() */
1117N/A
1117N/A
1117N/A/*
1117N/A * ListDevices()
1117N/A *
1117N/A * In response to the -list option, display the frame buffer device
1117N/A * files found in the /dev/fbs directory, along with the frame
1117N/A * buffer configuration program appropriate for the type of device.
1117N/A * If there is some obvious problem with the device or the config
1117N/A * program, a diagnostic message may be displayed instead of the
1117N/A * program file name.
1117N/A *
1117N/A * Illustrative -list output, assuming Xorg is configured:
1117N/A *
1117N/A * Device File Name Device Model Config Program
1117N/A * ---------------- ------------ --------------
1117N/A * /dev/fbs/cgsix0 not configurable
1117N/A * /dev/fbs/ffb0 not configurable
1117N/A * /dev/fbs/ffb1 not configurable
1117N/A * /dev/fbs/kfb0 XVR-2500 SUNWkfb_config
1117N/A * /dev/fbs/kfb1 XVR-2500 SUNWkfb_config
1117N/A * /dev/fbs/m640 not configurable
1117N/A * /dev/fbs/nfb0 [a|b] XVR-300 not configurable
1117N/A * /dev/fbs/pfb0 [a|b] XVR-100 not configurable
1117N/A * /dev/fbs/efb0 XVR-300 SUNWefb_config
1117N/A * /dev/fbs/efb1 XVR-100 SUNWefb_config
1117N/A * /dev/fbx/ast0 AST-2100 SUNWast_config
1117N/A *
1117N/A * This and the FindConfigurableDevices() function are similar and
1117N/A * can be maintained together.
1117N/A */
1117N/A
1117N/A/* Entry in a sorted, singly-linked list of potential device names */
1117N/Atypedef struct devent_st {
1117N/A struct devent_st *next; /* Ptr to next device entry in list */
1117N/A char name[1]; /* Name of potential device file */
1117N/A} dev_ent_t;
1117N/A
1117N/Astatic
1117N/Aunsigned int
1117N/Aget_device_mask(char * device_path)
1117N/A{
1117N/A if (strstr(device_path, "ast")) {
1117N/A return GFX_DEV_AST;
1117N/A } else if (strstr(device_path, "efb")) {
1117N/A return (GFX_DEV_EFB | GFX_DEV_NFB | GFX_DEV_PFB);
1117N/A } else if (strstr(device_path, "nfb")) {
1117N/A return GFX_DEV_NFB;
1117N/A } else if (strstr(device_path, "pfb")) {
1117N/A return GFX_DEV_PFB;
1117N/A } else if (strstr(device_path, "kfb")) {
1117N/A return GFX_DEV_KFB;
1117N/A } else if (strstr(device_path, "ifb")) {
1117N/A return GFX_DEV_IFB;
1117N/A } else if (strstr(device_path, "jfb")) {
1117N/A return GFX_DEV_JFB;
1117N/A } else if (strstr(device_path, "ffb")) {
1117N/A return GFX_DEV_FFB;
1117N/A } else if (strstr(device_path, "m64")) {
1117N/A return GFX_DEV_M64;
1117N/A } else {
1117N/A return 0;
1117N/A }
1117N/A}
1117N/A
1117N/Astatic
1117N/Avoid
1117N/AListDevices(
1117N/A xserv_t x_server, /* Xserver: Xsun, Xorg */
1117N/A boolean_t probe_only, /* 1 - probe the devices only, no output */
1117N/A unsigned int *device_mask) /* bit set for each listed device */
1117N/A{
1117N/A char device_model[GFX_MAX_MODELNAME_LEN]; /* Model_name */
1117N/A char device_path[MAX_DEV_PATH_LEN + MAX_SUFFIX_LIST_LEN];
1117N/A struct dirent *dir_entry; /* Device directory entry */
1117N/A DIR *dir_fd; /* Device directory file descriptor */
1117N/A size_t dir_len; /* Length of "/dev/fbs/" */
1117N/A dev_ent_t *dev_list; /* Head of device name list */
1117N/A dev_ent_t *dev_entry; /* Ptr to current device name entry */
1117N/A dev_ent_t **dev_ent_pptr; /* Ptr to ptr to current entry */
1117N/A dev_ent_t *dev_next; /* Ptr to next device name entry */
1117N/A size_t file_len; /* Length of simple filename */
1117N/A boolean_t found = B_FALSE; /* TRUE => Device(s) displayed */
1117N/A
1117N/A /*
1117N/A * Open the /dev/fbs device directory
1117N/A */
1117N/A dir_fd = opendir(DEVICE_DIR);
1117N/A if (dir_fd == NULL) {
1117N/A PrintError(catgets(catfd, 6, 1,
1117N/A "Cannot open the " DEVICE_DIR " directory"));
1117N/A exit(1);
1117N/A }
1117N/A
1117N/A /*
1117N/A * Insert each file name in the /dev/fbs directory into a sorted list
1117N/A */
1117N/A dir_len = strlen(DEVICE_DIR "/");
1117N/A dev_list = NULL;
1117N/A for (;;) {
1117N/A /*
1117N/A * Read the next directory entry
1117N/A */
1117N/A dir_entry = readdir(dir_fd);
1117N/A if (dir_entry == NULL) {
1117N/A if ((errno != 0) && (errno != ENOENT)) {
1117N/A PrintError("%s, while reading " DEVICE_DIR,
1117N/A strerror(errno));
1117N/A }
1117N/A break;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Ignore anything that clearly isn't a device name
1117N/A */
1117N/A if ((strcmp(dir_entry->d_name, ".") == 0) ||
1117N/A (strcmp(dir_entry->d_name, "..") == 0)) {
1117N/A continue; /* Directories aren't devices */
1117N/A }
1117N/A file_len = strlen(dir_entry->d_name);
1117N/A if (dir_len + file_len >= MAX_DEV_PATH_LEN) {
1117N/A continue; /* Too long for a device pathname */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Create a list entry for this potential device name
1117N/A */
1117N/A dev_entry = malloc(sizeof(dev_ent_t) + file_len);
1117N/A if (dev_entry == NULL) {
1117N/A PrintError("Insufficient memory, %s",
1117N/A dir_entry->d_name);
1117N/A exit(1);
1117N/A }
1117N/A strcpy(dev_entry->name, dir_entry->d_name);
1117N/A
1117N/A /*
1117N/A * Insert the new name into the sorted list of names
1117N/A */
1117N/A for (dev_ent_pptr = &dev_list;
1117N/A ;
1117N/A dev_ent_pptr = &dev_next->next) {
1117N/A dev_next = *dev_ent_pptr;
1117N/A if ((dev_next == NULL) ||
1117N/A (strcmp(dev_entry->name, dev_next->name) < 0)) {
1117N/A dev_entry->next = dev_next;
1117N/A *dev_ent_pptr = dev_entry;
1117N/A break;
1117N/A }
1117N/A }
1117N/A }
1117N/A
1117N/A closedir(dir_fd);
1117N/A
1117N/A /*
1117N/A * Display information for each list entry that looks like a device
1117N/A */
1117N/A strncpy(device_path, DEVICE_DIR "/", MAX_DEV_PATH_LEN);
1117N/A for (dev_entry = dev_list; dev_entry != NULL; dev_entry = dev_next) {
1117N/A char *config_prog_path; /* Device config program pathname */
1117N/A int error_code; /* Error code (see errno.h) */
1117N/A const char *program_field; /* Config prog name or msg text */
1117N/A char suffix_char; /* Stream suffix char, else Nul */
1117N/A char *suffix_ptr; /* Suffix list ptr in dev path buf */
1117N/A char suffix_punct; /* Punctuation for suffix char list */
1117N/A int st_rdev; /* st_rdev value returned by stat() */
1117N/A struct vis_identifier vis_ident; /* Device vis identifier */
1117N/A
1117N/A /*
1117N/A * Construct the full pathname for this potential device
1117N/A *
1117N/A * Note that this pathname length was compared to the
1117N/A * buffer length in the previous loop. Oversized
1117N/A * names were rejected.
1117N/A */
1117N/A file_len = strlen(dev_entry->name);
1117N/A strcpy(&device_path[dir_len], dev_entry->name);
1117N/A
1117N/A /*
1117N/A * Discard this list entry
1117N/A */
1117N/A dev_next = dev_entry->next;
1117N/A free(dev_entry);
1117N/A
1117N/A /*
1117N/A * See if this file is a device and get its identifications
1117N/A */
1135N/A device_model[0] = '\0';
1117N/A error_code = get_device_identification(
1117N/A device_path,
1117N/A &st_rdev, &vis_ident, &device_model[0]);
1117N/A
1117N/A if (error_code == 0) {
1117N/A *device_mask |= get_device_mask(device_path);
1117N/A }
1117N/A
1117N/A if (probe_only == B_TRUE)
1117N/A continue;
1117N/A
1117N/A /*
1117N/A * Determine the "Config Program" field contents
1117N/A */
1117N/A config_prog_path = NULL; /* For free() */
1117N/A program_field = ""; /* For falling thru cracks */
1117N/A if (error_code != 0) {
1117N/A if (error_code != EACCES) {
1117N/A continue; /* Not a convincing device */
1117N/A }
1117N/A /* No VISUAL env identifier with which to proceed */
1117N/A program_field = "device access denied";
1117N/A } else {
1117N/A /*
1117N/A * Get the config program pathname for this device
1117N/A */
1117N/A error_code = GetConfigProgramPath(x_server,
1117N/A vis_ident.name,
1117N/A &config_prog_path);
1117N/A if (error_code != 0) {
1117N/A /*
1117N/A * Provide a diagnostic message
1117N/A */
1117N/A switch (error_code) {
1117N/A case EINVAL: /* Invalid x_server value */
1117N/A program_field = catgets(catfd, 7, 1,
1117N/A "X server not known");
1117N/A break;
1117N/A case ENOENT: /* Config program not found */
1117N/A program_field = catgets(catfd, 7, 1,
1117N/A "program not available");
1117N/A break;
1117N/A case EACCES: /* User can't access program */
1117N/A program_field =
1117N/A "program access denied";
1117N/A break;
1117N/A default: /* Some other mischief */
1117N/A program_field = strerror(error_code);
1117N/A break;
1117N/A }
1117N/A } else {
1117N/A /*
1117N/A * Find the simple filename of the config prog
1117N/A */
1117N/A program_field = strrchr(config_prog_path, '/');
1117N/A if (program_field == NULL) {
1117N/A program_field = config_prog_path;
1117N/A } else {
1117N/A program_field += 1;
1117N/A }
1117N/A }
1117N/A }
1117N/A
1117N/A /*
1117N/A * Check for streams that can be consolidated with this device
1117N/A *
1117N/A * It is assumed that the device name list is sorted
1117N/A * such that all names for a given device are
1117N/A * adjacent, with the plain device name first and any
1117N/A * stream-suffixed names following. It's also
1117N/A * assumed that a stream name is the device name with
1117N/A * a one-character suffix from the set,
1117N/A * STREAM_SUFFIX_CHARS.
1117N/A */
1117N/A suffix_ptr = &device_path[dir_len + file_len]; /* The Nul */
1117N/A suffix_punct = '[';
1117N/A while (dev_next != NULL) {
1117N/A /*
1117N/A * See if the next list entry has a related stream name
1117N/A */
1117N/A dev_entry = dev_next;
1117N/A suffix_char = ListDevices_StreamCheck(
1117N/A &device_path[dir_len],
1117N/A st_rdev,
1117N/A &vis_ident,
1117N/A dev_entry->name);
1117N/A if (suffix_char == '\0') {
1117N/A break; /* Not a related stream name */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Append this stream suffix character to the substring
1117N/A */
1117N/A *(suffix_ptr+1) = suffix_punct; /* '[' or '|' */
1117N/A *(suffix_ptr+2) = suffix_char; /* 'a', 'b', ... */
1117N/A suffix_ptr += 2;
1117N/A suffix_punct = '|';
1117N/A
1117N/A /*
1117N/A * Free this now-consolidated stream entry
1117N/A */
1117N/A dev_next = dev_entry->next;
1117N/A free(dev_entry);
1117N/A }
1117N/A if (suffix_punct != '[') {
1117N/A /*
1117N/A * Terminate the non-empty stream suffix char substring
1117N/A */
1117N/A *(suffix_ptr+1) = ']';
1117N/A *(suffix_ptr+2) = '\0';
1117N/A
1117N/A /*
1117N/A * Append the stream suffix character substring
1117N/A */
1117N/A device_path[dir_len + file_len] = ' '; /* Was Nul */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Display the file & any streams, model, and config prog field
1117N/A */
1117N/A if (!found) {
1117N/A found = B_TRUE;
1117N/A#ifdef DEBUG_FB /* Show VISUAL environment identifier also */
1117N/A printf(" -------");
1117N/A#endif
1117N/A printf(catgets(catfd, 8, 1,
1117N/A " Device File Name Device Model Config Program\n"
1117N/A " ---------------- ------------ --------------\n"));
1117N/A }
1117N/A#ifdef DEBUG_FB /* Show VISUAL environment identifier also */
1117N/A printf(" %-7.7s", vis_ident.name);
1117N/A#endif
1117N/A printf(" %-27.27s %-16.16s %s\n",
1117N/A device_path, device_model, program_field);
1117N/A
1117N/A free(config_prog_path);
1117N/A }
1117N/A
1117N/A /*
1117N/A * If nothing was found then say so (before returning and terminating)
1117N/A */
1117N/A if (!probe_only && !found) {
1117N/A printf(catgets(catfd, 9, 1,
1117N/A "No configurable devices found in "
1117N/A DEVICE_DIR " directory\n"));
1117N/A }
1117N/A
1117N/A} /* ListDevices() */
1117N/A
1117N/A
1117N/A/*
1117N/A * FindConfigurableDevices()
1117N/A *
1117N/A * This function checks /dev/fb and searches /dev/fbs until a
1117N/A * frame buffer device is found that can be opened and provide a
1117N/A * VISUAL environment device identifier. The possible return values
1117N/A * are:
1117N/A * Zero - Success; at least one device exists and can be accessed
1117N/A * ENOENT - No frame buffer device was found
1117N/A * EACCES - Something was found, but the user can't access it
1117N/A *
1117N/A * The -gui option will not launch the DCM Tool if this function
1117N/A * returns a non-zero value.
1117N/A *
1117N/A * This and the ListDevices() function are similar and can be
1117N/A * maintained together.
1117N/A */
1117N/A
1117N/Astatic
1117N/Aint
1117N/AFindConfigurableDevices(void)
1117N/A{
1117N/A char *default_path; /* Default device pathname (dummy) */
1117N/A char device_path[MAX_DEV_PATH_LEN];
1117N/A struct dirent *dir_entry; /* Device directory entry */
1117N/A DIR *dir_fd; /* Device directory file descriptor */
1117N/A size_t dir_len; /* Length of "/dev/fbs/" */
1117N/A int err_code; /* Error code (see errno.h) */
1117N/A int error_code; /* Return EACCES, ENOENT, or zero */
1117N/A size_t file_len; /* Length of simple filename */
1117N/A struct vis_identifier vis_ident; /* Device VISUAL env identifier */
1117N/A
1117N/A error_code = ENOENT;
1117N/A
1117N/A /*
1117N/A * See whether any default device will open and identify itself
1117N/A */
1117N/A err_code = GetDefaultDevicePathname(&default_path);
1117N/A if (err_code == 0) {
1117N/A return (0); /* At least one FB device exists */
1117N/A }
1117N/A if (err_code == EACCES) {
1117N/A error_code = EACCES; /* Caller may have to complain */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Open the /dev/fbs device directory
1117N/A */
1117N/A dir_fd = opendir(DEVICE_DIR);
1117N/A if (dir_fd == NULL) {
1117N/A PrintError(catgets(catfd, 6, 1,
1117N/A "Cannot open the " DEVICE_DIR " directory"));
1117N/A exit(1);
1117N/A }
1117N/A
1117N/A /*
1117N/A * Examine each file in /dev/fbs until a working FB device is found
1117N/A */
1117N/A dir_len = strlen(DEVICE_DIR "/");
1117N/A for (;;) {
1117N/A /*
1117N/A * Read the next directory entry
1117N/A */
1117N/A dir_entry = readdir(dir_fd);
1117N/A if (dir_entry == NULL) {
1117N/A if ((errno != 0) && (errno != ENOENT)) {
1117N/A PrintError("%s, while reading " DEVICE_DIR,
1117N/A strerror(errno));
1117N/A }
1117N/A break;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Ignore anything that clearly isn't a device
1117N/A */
1117N/A if ((strcmp(dir_entry->d_name, ".") == 0) ||
1117N/A (strcmp(dir_entry->d_name, "..") == 0)) {
1117N/A continue; /* Directories aren't devices */
1117N/A }
1117N/A file_len = strlen(dir_entry->d_name);
1117N/A if (dir_len + file_len >= MAX_DEV_PATH_LEN) {
1117N/A continue; /* Too long for a device pathname */
1117N/A }
1117N/A
1117N/A /*
1117N/A * Construct the full pathname for the device
1117N/A */
1117N/A strcpy(&device_path[dir_len], dir_entry->d_name);
1117N/A
1117N/A /*
1117N/A * See if this file is a frame buffer device
1117N/A */
1117N/A err_code = get_device_identification(
1117N/A device_path, NULL, &vis_ident, NULL);
1117N/A if (err_code == 0) {
1117N/A error_code = 0;
1117N/A break; /* At least one FB device exists */
1117N/A }
1117N/A if (err_code == EACCES) {
1117N/A error_code = EACCES; /* Caller may have to complain */
1117N/A }
1117N/A }
1117N/A
1117N/A closedir(dir_fd);
1117N/A
1117N/A return (error_code);
1117N/A
1117N/A} /* FindConfigurableDevices() */
1117N/A
1117N/A
1117N/A/*
1117N/A * InvokeGUI()
1117N/A *
1117N/A * Implement the -gui option:
1117N/A * -gui Executes the Graphics User Interface program DCMTool
1117N/A * if it is available.
1117N/A *
1117N/A * This is done by invoking:
1117N/A * pfexec /usr/lib/fbconfig/SUNWdcm/bin/dcmtool
1117N/A * (RBAC's exec_attr entry makes that program setuid root.)
1117N/A *
1117N/A * If successful, this function does not return. A non-zero exit
1117N/A * code is returned otherwise.
1117N/A */
1117N/A
1117N/Astatic
1117N/Aint
1117N/AInvokeGUI(void)
1117N/A{
1117N/A const char *pfexec_path = "/usr/bin/pfexec";
1117N/A const char *gui_path; /* DCM Tool GUI script pathname */
1117N/A int error_code; /* Error code (see errno.h) */
1117N/A
1117N/A /*
1117N/A * Make sure the GUI will have at least one device
1117N/A *
1117N/A * The error_code returned by FindConfigurableDevices()
1117N/A * will be EACCES, ENOENT, or zero.
1117N/A */
1117N/A error_code = FindConfigurableDevices();
1117N/A if (error_code != 0) {
1117N/A if (error_code == EACCES) {
1117N/A PrintError(catgets(catfd, 21, 2,
1117N/A "You do not have access to configure"
1117N/A " graphics devices on this system.\n"
1117N/A "You will have access if you log in"
1117N/A " to the console or you are superuser (root)."));
1117N/A return (2);
1117N/A }
1117N/A printf(catgets(catfd, 9, 1,
1117N/A "No configurable devices found in "
1117N/A DEVICE_DIR " directory\n"));
1117N/A return (1);
1117N/A }
1117N/A
1117N/A /*
1117N/A * Get the pathname of the DCM Tool GUI script
1117N/A */
1117N/A error_code = PathToGUI(&gui_path);
1117N/A if (error_code != 0) {
1117N/A if (error_code == ENOENT) {
1117N/A PrintError(catgets(catfd, 20, 1,
1117N/A "GUI not present. Install SUNWdcm package."));
1117N/A } else {
1117N/A PrintError("%s, %s", strerror(error_code), gui_path);
1117N/A }
1117N/A return (1);
1117N/A }
1117N/A
1117N/A (void) execl(pfexec_path, pfexec_path, gui_path, NULL);
1117N/A
1117N/A /* execl() should not return; if it did, it failed */
1117N/A#ifdef DEBUG_FB
1117N/A PrintError("execl() failed, errno=%d: %s", errno, strerror(errno));
1117N/A#endif
1117N/A return (2);
1117N/A
1117N/A} /* InvokeGUI() */
1117N/A
1117N/A
1117N/Astatic
1117N/Aint
1117N/AIsXserverSupported(xserv_t xserver_arg, unsigned int device_mask)
1117N/A{
1117N/A struct stat stat_buf; /* stat() buffer for device file */
1117N/A
1117N/A if (stat(xserver_path[xserver_arg], &stat_buf) != 0) {
1117N/A printf("Fails to configure xserver to %s, no such file\n", xserver_path[xserver_arg]);
1117N/A return 0;
1117N/A }
1117N/A
1117N/A if (device_mask & ~xserver_device[xserver_arg]) {
1117N/A printf("Fails to configure xserver to %s, not all graphics devices in the system can be configured to run with this xserver\n",
1117N/A xserver_path[xserver_arg]);
1117N/A return 0;
1117N/A }
1117N/A
1117N/A return 1;
1117N/A}
1117N/A
1117N/Astatic
1117N/Avoid
1117N/ASetXServer(xserv_t x_server, xserv_t xserver_arg, unsigned int device_mask)
1117N/A{
1117N/A struct stat stat_buf; /* stat() buffer for device file */
1117N/A char efb_path[40] = "/kernel/drv/sparcv9/efb";
1117N/A char nfb_path[40] = "/kernel/drv/sparcv9/nfb";
1117N/A char pfb_path[40] = "/kernel/drv/sparcv9/pfb";
1117N/A
1117N/A if (xserver_arg == XSERVER_XORG) {
1117N/A
1117N/A /*
1117N/A * First check if all the listed devices supports Xorg.
1117N/A * If not, setting to Xorg will be denied.
1117N/A */
1117N/A if (!IsXserverSupported(xserver_arg, device_mask)) {
1117N/A return;
1117N/A }
1117N/A
1117N/A /*
1117N/A * If there is a XVR-50, XVR-100, XVR-300, check if efb driver exists
1117N/A */
1117N/A if (device_mask & (GFX_DEV_NFB | GFX_DEV_PFB | GFX_DEV_EFB)) {
1117N/A if (stat(efb_path, &stat_buf) != 0) {
1117N/A printf("Fails to configure xserver to %s, efb driver is not installed\n",
1117N/A xserver_path[xserver_arg]);
1117N/A return;
1117N/A }
1117N/A }
1117N/A
1208N/A system("svccfg -s svc:/application/x11/x11-server setprop options/server=/usr/bin/Xorg");
1117N/A
1117N/A if (stat(efb_path, &stat_buf) == 0) {
1117N/A system("rem_drv nfb 2>/dev/null&");
1117N/A system("rem_drv pfb 2>/dev/null&");
1117N/A system("add_drv -n -m '* 0666 root sys' -i \"SUNW,XVR-50 SUNW,XVR-100 SUNW,XVR-300\" efb 2>/dev/null&");
1117N/A printf("A reboot needs to be performed to complete the reconfiguration to run Xorg\n");
1117N/A } else {
1117N/A printf("Fails to switch to efb driver. Please check to see if SUNWefb is installed.\n");
1117N/A }
1117N/A
1117N/A } else if (xserver_arg == XSERVER_XSUN) {
1117N/A
1117N/A int pfb_exists = 0;
1117N/A
1117N/A /*
1117N/A * First check if all the listed devices supports Xsun.
1117N/A * If not, setting to Xsun will be denied.
1117N/A */
1117N/A if (!IsXserverSupported(xserver_arg, device_mask)) {
1117N/A return;
1117N/A }
1117N/A
1117N/A#if OSVER==510
1117N/A if ((stat("/platform/sun4u/kernel/drv/sparcv9/pfb", &stat_buf) == 0) ||
1117N/A (stat("/platform/sun4v/kernel/drv/sparcv9/pfb", &stat_buf) == 0) ||
1117N/A (stat("/platform/sun4us/kernel/drv/sparcv9/pfb", &stat_buf) == 0)) {
1117N/A pfb_exists = 1;
1117N/A }
1117N/A#else
1117N/A if (stat(pfb_path, &stat_buf) == 0) {
1117N/A pfb_exists = 1;
1117N/A}
1117N/A#endif
1117N/A
1117N/A /*
1117N/A * If there is a XVR-300, check if nfb driver exists
1117N/A */
1117N/A if (device_mask & GFX_DEV_NFB) {
1117N/A if (stat(nfb_path, &stat_buf) != 0) {
1117N/A printf("Fails to configure xserver to %s, nfb driver is not installed\n",
1117N/A xserver_path[xserver_arg]);
1117N/A return;
1117N/A }
1117N/A }
1117N/A
1117N/A /*
1117N/A * If there is a XVR-50, XVR-100, check if pfb driver exists
1117N/A */
1117N/A if (device_mask & GFX_DEV_PFB) {
1117N/A if (pfb_exists == 0) {
1117N/A printf("Fails to configure xserver to %s, pfb driver is not installed\n",
1117N/A xserver_path[xserver_arg]);
1117N/A return;
1117N/A }
1117N/A }
1117N/A
1117N/A system("svccfg -s svc:/application/x11/x11-server setprop options/server=/usr/openwin/bin/Xsun");
1117N/A
1117N/A if (stat(efb_path, &stat_buf) == 0) {
1117N/A system("rem_drv efb 2>/dev/null&");
1117N/A }
1117N/A
1117N/A if (stat(nfb_path, &stat_buf) == 0) {
1117N/A system("add_drv -n -m '* 0666 root sys' -i \"SUNW,XVR-300\" nfb 2>/dev/null&");
1117N/A }
1117N/A
1117N/A if (pfb_exists) {
1117N/A system("add_drv -n -m '* 0666 root sys' -i \"SUNW,XVR-50 SUNW,XVR-100\" pfb 2>/dev/null&");
1117N/A }
1117N/A
1117N/A printf("A reboot needs to be performed to complete the reconfiguration to run Xsun\n");
1117N/A
1117N/A } else {
1117N/A printf("Default Xserver: %s\n", xserver_path[x_server]);
1117N/A }
1117N/A} /* SetXServer() */
1117N/A
1117N/A
1117N/A/*
1117N/A * main() for fbconfig(1M)
1117N/A *
1117N/A * Do exactly one of the following, in order of precedence, depending
1117N/A * on options in the program command line (argv[]):
1117N/A * * Display fbconfig(1M) help text (-help) along with any
1117N/A * configuration program help text (-dev, -help) and then
1117N/A * terminate.
1117N/A * * Display the installed frame buffer devices and the
1117N/A * corresponding configuration program for each (-list) and then
1117N/A * terminate.
1117N/A * * Become the dcmtool GUI (-gui).
1117N/A * * Repackage the program argument vector and become the
1117N/A * appropriate device configuration program (-dev).
1117N/A * * Report a fatal error and terminate.
1117N/A */
1117N/A
1117N/Aint
1117N/Amain(
1117N/A int argc, /* Program argument count */
1117N/A char *argv[]) /* Program argument vector */
1117N/A{
1117N/A#define CFG_ARG_1 3 /* Index of initial cfg_argv[] arg */
1117N/A boolean_t dev_opt = B_FALSE; /* TRUE => Found -dev option */
1117N/A boolean_t help_opt = B_FALSE; /* TRUE => Found -help option */
1117N/A boolean_t list_opt = B_FALSE; /* TRUE => Found -list option */
1117N/A boolean_t gui_opt = B_FALSE; /* TRUE => Found -gui option */
1117N/A boolean_t xserver_opt = B_FALSE; /* TRUE => Found -xserver option */
1117N/A char **cfg_argv; /* Config program argument vector */
1117N/A char *config_prog_path; /* Config program pathname */
1117N/A char *device_path; /* Device pathname */
1117N/A char device_path_buf[MAX_DEV_PATH_LEN];
1117N/A int i; /* argv[] argument vector index */
1117N/A int n; /* cfg_argv[] argument vector index */
1117N/A xserv_t x_server; /* X server: Xsun, Xorg, ... */
1117N/A xserv_t xserver_arg; /* -xserver arg: Xsun, Xorg, ... */
1117N/A unsigned int device_mask = 0;
1117N/A
1117N/A catfd = catopen("fbconfig", 0);
1117N/A
1117N/A /*
1117N/A * Allocate an argument vector to pass to the device config program
1117N/A *
1117N/A * The config program (child) argument vector must be large
1117N/A * enough to hold:
1117N/A * * The config program pathname ( 1 pointer )
1117N/A * * A default "-dev /dev/fb" option ( 2 pointers)
1117N/A * * The fbconfig(1M) arguments (argc-1 pointers)
1117N/A * * A NULL terminator ( 1 pointer )
1117N/A */
1117N/A cfg_argv = malloc((2 + argc + 1) * sizeof (char *));
1117N/A if (cfg_argv == NULL) {
1117N/A PrintError("Insufficient memory for new argument vector");
1117N/A return (1);
1117N/A }
1117N/A
1117N/A /*
1117N/A * Process the fbconfig(1M) program command line arguments
1117N/A *
1117N/A * Leave room in the child's argument vector to prepend these
1117N/A * three (CFG_ARG_1) strings:
1117N/A * * The pathname of the child config program
1117N/A * * A -dev option and a default frame buffer name
1117N/A */
1117N/A n = CFG_ARG_1;
1117N/A for (i = 1; i < argc; i += 1) {
1117N/A /* Look for -help (for which -dev can be a modifier) */
1117N/A if (strncmp(argv[i], "-help", 6) == 0) {
1117N/A help_opt = B_TRUE;
1117N/A continue; /* Option isn't passed to child */
1117N/A }
1117N/A
1117N/A /* Look for a -list option and don't copy it */
1117N/A if (strncmp(argv[i], "-list", 6) == 0) {
1117N/A list_opt = B_TRUE;
1117N/A continue; /* Option isn't passed to child */
1117N/A }
1117N/A
1117N/A /* Look for a -list option and don't copy it */
1117N/A if (strncmp(argv[i], "-xserver", 9) == 0) {
1117N/A xserver_opt = B_TRUE;
1117N/A xserver_arg = XSERVER_UNKNOWN;
1117N/A
1117N/A i++;
1117N/A if (i < argc) {
1117N/A if (strncasecmp(argv[i], "Xsun", 4) == 0) {
1117N/A xserver_arg = XSERVER_XSUN;
1117N/A } else if (strncasecmp(argv[i], "Xorg", 4) == 0) {
1117N/A xserver_arg = XSERVER_XORG;
1117N/A }
1117N/A }
1117N/A continue; /* Option isn't passed to child */
1117N/A }
1117N/A
1117N/A /* Look for a -gui option and don't copy it */
1117N/A if (strncmp(argv[i], "-gui", 5) == 0) {
1117N/A gui_opt = B_TRUE;
1117N/A continue; /* Option isn't passed to child */
1117N/A }
1117N/A
1117N/A /* Look for -dev and save the full device pathname */
1117N/A if (strncmp(argv[i], "-dev", 5) == 0) {
1117N/A /*
1117N/A * Check for -dev option errors
1117N/A */
1117N/A i += 1;
1117N/A if (i >= argc) {
1117N/A PrintError(catgets(catfd, 11, 1,
1117N/A "Option requires a value, -dev"));
1117N/A return (1);
1117N/A }
1117N/A if (dev_opt) {
1117N/A PrintError(catgets(catfd, 10, 1,
1117N/A "Duplicate option, -dev %s"),
1117N/A argv[i]);
1117N/A return (1);
1117N/A }
1117N/A dev_opt = B_TRUE;
1117N/A
1117N/A /*
1117N/A * Get the fully qualified device pathname
1117N/A */
1117N/A device_path = GetDevicePathname(
1117N/A argv[i],
1117N/A device_path_buf,
1117N/A sizeof (device_path_buf));
1117N/A
1117N/A /*
1117N/A * Copy the -dev option to the new argument vector
1117N/A */
1117N/A cfg_argv[n ] = argv[i-1];
1117N/A cfg_argv[n+1] = device_path;
1117N/A n += 2;
1117N/A
1117N/A continue;
1117N/A }
1117N/A
1117N/A /*
1117N/A * Copy this string to the arg vector for the config program
1117N/A */
1117N/A cfg_argv[n] = argv[i];
1117N/A n += 1;
1117N/A }
1117N/A cfg_argv[n] = NULL;
1117N/A
1117N/A /*
1117N/A * Find out which X server is configured (Xsun, Xorg, ...)
1117N/A *
1117N/A * If the X server isn't known, the GetConfigProgramPath()
1117N/A * function can't return a configuration program pathname.
1117N/A * Options such as -help and -list might still be useful,
1117N/A * however (and someday -gui will work too).
1117N/A */
1117N/A x_server = IdentifyXServer();
1117N/A if (x_server == XSERVER_UNKNOWN) {
1117N/A PrintError("Unable to identify the configured X server");
1117N/A }
1117N/A
1117N/A /*
1117N/A * Try to make sure the frame buffer device pathname is known
1117N/A */
1117N/A if (!dev_opt) {
1117N/A (void) GetDefaultDevicePathname(&device_path);
1117N/A }
1117N/A
1117N/A /*
1117N/A * If -help was specified or implied, display help text and exit
1117N/A *
1117N/A * The -help option is implied iff there is no -list or -gui
1117N/A * option and no option or argument intended for a device
1117N/A * configuration program (n > CFG_ARG_1). The -dev option is
1117N/A * either a modifier for the explicit -help option or is an
1117N/A * option for the device configuration program.
1117N/A */
1117N/A if (help_opt || !(list_opt || xserver_opt || gui_opt || (n > CFG_ARG_1))) {
1117N/A PrintHelp(x_server, device_path);
1117N/A return (0);
1117N/A }
1117N/A
1117N/A /*
1117N/A * If -list was specified, display the frame buffer devices and exit
1117N/A */
1117N/A if (list_opt) {
1117N/A ListDevices(x_server, B_FALSE, &device_mask);
1117N/A return (0);
1117N/A }
1117N/A
1117N/A /*
1117N/A *
1117N/A */
1117N/A if (xserver_opt) {
1117N/A ListDevices(x_server, B_TRUE, &device_mask);
1117N/A SetXServer(x_server, xserver_arg, device_mask);
1117N/A return (0);
1117N/A }
1117N/A
1117N/A /*
1117N/A * If -gui was specified, try to become the GUI (else exit)
1117N/A */
1117N/A if (gui_opt) {
1117N/A /*
1117N/A * Invoke the GUI (w/o returning), else exit unsuccessfully
1117N/A */
1117N/A#if (1) /* ??? Cover for temporarily absent functionality ??? */
1117N/A if (x_server != XSERVER_XSUN) {
1117N/A PrintError(
1117N/A "A GUI interface is available only with the Xsun server");
1117N/A return (1);
1117N/A }
1117N/A#endif
1117N/A return (InvokeGUI());
1117N/A }
1117N/A
1117N/A /*
1117N/A * Identify and invoke the device configuration program
1117N/A *
1117N/A * The default -dev option, if any, should be prepended
1117N/A * rather than appended to the argument vector. If the
1117N/A * user-supplied argument vector ends badly (e.g. the last
1117N/A * option's argument is missing), the child config program
1117N/A * should be able to diagnose and report the problem
1117N/A * correctly, without tripping over an appended -dev option.
1117N/A */
1117N/A if ((device_path != NULL) &&
1117N/A (FindConfigProgram(x_server, device_path, &config_prog_path) == 0)
1117N/A ) {
1117N/A n = 2; /* Index for config program pathname */
1117N/A if (!dev_opt) {
1117N/A n = 0; /* Index for config program pathname */
1117N/A cfg_argv[1] = "-dev";
1117N/A cfg_argv[2] = device_path;
1117N/A }
1117N/A cfg_argv[n] = config_prog_path;
1117N/A CallConfigProgram(&cfg_argv[n]);
1117N/A } else {
1117N/A PrintError("Unable to find a default device");
1117N/A }
1117N/A
1117N/A catclose(catfd); /* Needn't have passed this to a child */
1117N/A
1117N/A return (1); /* Exit unsuccessfully */
1117N/A
1117N/A} /* main() */
1117N/A
1117N/A
1117N/A/* End of fbconfig.c */