/* prom - prom handling routines via /dev/openprom Copyright (C) 1996 Jakub Jelinek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #ifdef __linux__ # include #elif defined (__solaris__) # include # include # include # include #else # error "Unknown system" #endif #include static struct openpromio *oi; static char buffer[2048 + sizeof (oi->oprom_size)]; static int prom_root_node = 0; static int fd; static int current_node; static int previous_node; static int action; static int parent_node; static char namebuf[2048]; #define SETSIZE oi->oprom_size = 2048; int prom_init (void) { fd = open ("/dev/openprom", O_RDONLY); if (fd < 0) return -1; oi = (struct openpromio *)buffer; SETSIZE *(int *)(oi->oprom_array) = 0; if (ioctl (fd, OPROMNEXT, (void *)oi) >= 0) { prom_root_node = *(int *)(oi->oprom_array); current_node = prom_root_node; previous_node = 0; parent_node = 0; action = 0; return 0; } return -1; } int prom_set_root_node (void) { SETSIZE *(int *)(oi->oprom_array) = 0; if (ioctl (fd, OPROMNEXT, (void *)oi) >= 0) { current_node = prom_root_node; previous_node = 0; parent_node = 0; action = 0; return 0; } return -1; } int prom_search_siblings (char *name) { int node = current_node; for (;;) { SETSIZE strcpy (oi->oprom_array, "name"); if (ioctl (fd, OPROMGETPROP, (void *)oi) < 0) return -1; if (!oi->oprom_size) return -1; if (!strcmp (oi->oprom_array, name)) return node; SETSIZE *(int *)(oi->oprom_array) = node; if (ioctl (fd, OPROMNEXT, (void *)oi) < 0 || !(*(int *)(oi->oprom_array))) return -1; if (!(*(int *)(oi->oprom_array)) || !oi->oprom_size) return -1; node = *(int *)(oi->oprom_array); previous_node = current_node; current_node = node; action = 0; } } int prom_next_sibling (void) { SETSIZE *(int *)(oi->oprom_array) = current_node; if (ioctl (fd, OPROMNEXT, (void *)oi) < 0 || !(*(int *)(oi->oprom_array))) return -1; if (!(*(int *)(oi->oprom_array)) || !oi->oprom_size) return -1; previous_node = current_node; current_node = *(int *)(oi->oprom_array); action = 0; return 0; } int prom_getchild (void) { SETSIZE *(int *)(oi->oprom_array) = current_node; if (ioctl (fd, OPROMCHILD, (void *)oi) >= 0) { if (!(*(int *)(oi->oprom_array)) || !oi->oprom_size) return -1; parent_node = current_node; current_node = *(int *)(oi->oprom_array); action = 1; previous_node = parent_node; return 0; } return -1; } char *prom_getstring (char *name) { SETSIZE strcpy (oi->oprom_array, name); if (ioctl (fd, OPROMGETPROP, (void *)oi) >= 0) { if (!oi->oprom_size) return 0; return oi->oprom_array; } return 0; } char *prom_getopt (char *name) { SETSIZE strcpy (oi->oprom_array, name); if (ioctl (fd, OPROMGETOPT, (void *)oi) >= 0) { if (!oi->oprom_size) return 0; return oi->oprom_array; } return 0; } #ifdef __solaris__ int prom_getversion() { int i; char arch[10], *p; SETSIZE if (ioctl (fd, OPROMGETVERSION, (void *)oi) >= 0) { p = strchr (oi->oprom_array, '.'); if (p && p > oi->oprom_array && p[-1] >= '0' && p[-1] <= '9' && (p == oi->oprom_array + 1 || p[-2] < '0' || p[-2] > '9')) { switch (p[-1]) { case '0': case '1': return 0; case '2': case '3': return 2; } } printf ("Please report version string '%s' to jj@sunsite.mff.cuni.cz\n", oi->oprom_array); } /* Let's try brute force :)) */ if (prom_set_root_node () < 0) return -1; *arch = 0; if ((p = prom_getstring ("compatability")) != 0) strcpy (arch, p); else if ((p = prom_getstring ("compatible")) != 0) strcpy (arch, p); if (*arch && (!strcmp (arch, "sun4m") || !strcmp (arch, "sun4d") || !strcmp (arch, "sun4e"))) return 2; else if (!*arch) { p = prom_getopt ("boot-from"); if (p && (!strncmp (p, "sd(", 3) || !strncmp (p, "le(", 3) || !strncmp (p, "ie(", 3) || !strncmp (p, "fd(", 3) || !strncmp (p, "xd(", 3))) return 0; } else { if (prom_getchild () < 0) return -1; if (prom_search_siblings ("aliases") != -1) { p = prom_getstring ("disk"); if (!p) p = prom_getstring ("net"); if (!p) p = prom_getstring ("disk0"); if (p && *p == '/') { if (!strncmp (p + 1, "iommu@", 6) || !strncmp (p + 1, "iommu/", 6) || !strncmp (p + 1, "sbus@", 5) || !strncmp (p + 1, "sbus/", 5) || !strncmp (p + 1, "espdma@", 7) || !strncmp (p + 1, "espdma/", 7) || !strncmp (p + 1, "esp@", 4) || !strncmp (p + 1, "esp/", 4) || !strncmp (p + 1, "io-unit@", 8) || !strncmp (p + 1, "io-unit/", 8)) return 2; } } } return -1; } #endif static int prom_can (char *name, char *buf) { char *p = strchr (name, '/'), *q, *r, c; int top = current_node; char buffer [20], buffer2[2048]; int i, j; if (prom_getchild () < 0) return -1; if (!p) p = strchr (name, 0); c = *p; *p = 0; q = strchr (name, '@'); if (q) *q = 0; for (;;) { i = prom_search_siblings (name); if (i != -1) { r = prom_getstring ("reg"); if (r) sprintf (buffer, "%x,%x", (*(unsigned int *)r) & 0xf, *(((unsigned int *)r)+1)); if (q && (c || (strcmp (name, "sd") && strcmp (name, "st")))) { if (!r) i = -1; else if (strcmp (buffer, q + 1)) i = -1; } if (i != -1) { sprintf (buf, "/%s%s%s", name, r ? "@" : ((!c && q && (!strcmp (name, "sd") || !strcmp (name, "st"))) ? "@" : ""), r ? buffer : ((!c && q && (!strcmp (name, "sd") || !strcmp (name, "st"))) ? q + 1 : "")); if (c) { int previous = previous_node; int curaction = action; i = prom_can (p + 1, strchr (buf, 0)); if (i == -2) return -2; current_node = previous; if (curaction) { if (prom_getchild () < 0) return -2; } else { if (prom_next_sibling () < 0) return -2; } if (i != -1) { if (q) *q = '@'; *p = c; return 0; } } else { if (q) *q = '@'; *p = c; return 0; } } } else break; } if (q) *q = '@'; *p = c; current_node = top; if (prom_getchild () < 0) return -2; j = 0; for (;;) { int previous; if (!j) j = 1; else { if (prom_next_sibling () < 0) return -1; } previous = current_node; r = prom_getstring ("name"); if (!r) continue; strcpy (buffer2, r); r = prom_getstring ("reg"); if (r) sprintf (buffer, "%x,%x", (*(unsigned int *)r) & 0xf, *(((unsigned int *)r)+1)); sprintf (buf, "/%s%s%s", buffer2, r ? "@" : "", r ? buffer : ""); i = prom_can (name, strchr (buf, 0)); if (i == -2) return -2; if (!i) return 0; current_node = previous; } } char *prom_canon (char *name) { char buffer[2048]; char *p, *q; if (*name != '/') { if (prom_set_root_node () >= 0 && prom_getchild () >= 0 && prom_search_siblings ("aliases") != -1 && (p = prom_getstring (name)) != 0) { strcpy (buffer, p); name = buffer; } } if (prom_set_root_node () < 0) return 0; if (!*name || !strcmp (name, "/")) return "/"; switch (prom_can (*name == '/' ? name + 1 : name, namebuf)) { case -2: return 0; case -1: return 0; default: return namebuf; } }