/* silo.c: Conversions between SCSI and IDE disk names * and OpenPROM fully qualified paths. * * Modified for DrakX light integration. * Copyright (C) 1999, 2000 Jakub Jelinek * * This software may be freely redistributed under the terms of the GNU * public license. * * 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. */ #ifdef __sparc__ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #ifndef OPROMSETCUR #define OPROMSETCUR 0x20004FF0 #define OPROMPCI2NODE 0x20004FF1 #define OPROMPATH2NODE 0x20004FF2 #endif static int hasaliases; static char *promdev = "/dev/openprom"; static int promfd; static char sd_targets[10] = "31204567"; static int p1275 = 0; static int prom_root_node, prom_current_node; static int promvers; static void (*prom_walk_callback)(int node); static char prom_path[1024]; #define MAX_PROP 128 #define MAX_VAL (4096-128-4) static char buf[4096]; static char regstr[40]; #define DECL_OP(size) struct openpromio *op = (struct openpromio *)buf; op->oprom_size = (size) static int prom_setcur(int node) { DECL_OP(sizeof(int)); if (node == -1) return 0; *(int *)op->oprom_array = node; if (ioctl (promfd, OPROMSETCUR, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } static int prom_getsibling(int node) { DECL_OP(sizeof(int)); if (node == -1) return 0; *(int *)op->oprom_array = node; if (ioctl (promfd, OPROMNEXT, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } static int prom_getchild(int node) { DECL_OP(sizeof(int)); if (!node || node == -1) return 0; *(int *)op->oprom_array = node; if (ioctl (promfd, OPROMCHILD, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } static char * prom_getproperty(char *prop, int *lenp) { DECL_OP(MAX_VAL); strcpy (op->oprom_array, prop); if (ioctl (promfd, OPROMGETPROP, op) < 0) return 0; if (lenp) *lenp = op->oprom_size; return op->oprom_array; } static char * prom_getopt(char *var, int *lenp) { DECL_OP(MAX_VAL); strcpy (op->oprom_array, var); if (ioctl (promfd, OPROMGETOPT, op) < 0) return 0; if (lenp) *lenp = op->oprom_size; return op->oprom_array; } static void prom_setopt(char *var, char *value) { DECL_OP(MAX_VAL); strcpy (op->oprom_array, var); strcpy (op->oprom_array + strlen (var) + 1, value); ioctl (promfd, OPROMSETOPT, op); } static int prom_getbool(char *prop) { DECL_OP(0); *(int *)op->oprom_array = 0; for (;;) { op->oprom_size = MAX_PROP; if (ioctl(promfd, OPROMNXTPROP, op) < 0) return 0; if (!op->oprom_size) return 0; if (!strcmp (op->oprom_array, prop)) return 1; } } static int prom_pci2node(int bus, int devfn) { DECL_OP(2*sizeof(int)); ((int *)op->oprom_array)[0] = bus; ((int *)op->oprom_array)[1] = devfn; if (ioctl (promfd, OPROMPCI2NODE, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } static int prom_path2node(char *path) { DECL_OP(MAX_VAL); strcpy (op->oprom_array, path); if (ioctl (promfd, OPROMPATH2NODE, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } #define PW_TYPE_SBUS 1 #define PW_TYPE_PCI 2 #define PW_TYPE_EBUS 3 static void prom_walk(char *path, int parent, int node, int type) { int nextnode; int len, ntype = type; char *prop; prop = prom_getproperty("name", &len); if (prop && len > 0) { if ((!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) && !type) ntype = PW_TYPE_SBUS; else if (!strcmp(prop, "ebus") && type == PW_TYPE_PCI) ntype = PW_TYPE_EBUS; else if (!strcmp(prop, "pci") && !type) ntype = PW_TYPE_PCI; } *path = '/'; strcpy (path + 1, prop); prop = prom_getproperty("reg", &len); if (prop && len >= 4) { unsigned int *reg = (unsigned int *)prop; int cnt = 0; if (!p1275 || (type == PW_TYPE_SBUS)) sprintf (regstr, "@%x,%x", reg[0], reg[1]); else if (type == PW_TYPE_PCI) { if ((reg[0] >> 8) & 7) sprintf (regstr, "@%x,%x", (reg[0] >> 11) & 0x1f, (reg[0] >> 8) & 7); else sprintf (regstr, "@%x", (reg[0] >> 11) & 0x1f); } else if (len == 4) sprintf (regstr, "@%x", reg[0]); else { unsigned int regs[2]; /* Things get more complicated on UPA. If upa-portid exists, then address is @upa-portid,second-int-in-reg, otherwise it is @first-int-in-reg/16,second-int-in-reg (well, probably upa-portid always exists, but just to be safe). */ memcpy (regs, reg, sizeof(regs)); prop = prom_getproperty("upa-portid", &len); if (prop && len == 4) { reg = (unsigned int *)prop; sprintf (regstr, "@%x,%x", reg[0], regs[1]); } else sprintf (regstr, "@%x,%x", regs[0] >> 4, regs[1]); } for (nextnode = prom_getchild(parent); nextnode; nextnode = prom_getsibling(nextnode)) { prop = prom_getproperty("name", &len); if (prop && len > 0 && !strcmp (path + 1, prop)) cnt++; } if (cnt > 1) strcat (path, regstr); } prom_walk_callback(node); nextnode = prom_getchild(node); if (nextnode) prom_walk(strchr (path, 0), node, nextnode, ntype); nextnode = prom_getsibling(node); if (nextnode) prom_walk(path, parent, nextnode, type); } static int prom_init(int mode) { struct utsname u; promfd = open(promdev, mode); if (promfd == -1) return -1; prom_root_node = prom_getsibling(0); if (!prom_root_node) return -1; if (!uname (&u) && !strcmp (u.machine, "sparc64")) p1275 = 1; return 0; } #define SDSK_TYPE_IDE 1 #define SDSK_TYPE_SD 2 #define SDSK_TYPE_PLN 3 #define SDSK_TYPE_FC 4 static struct sdsk_disk { unsigned int prom_node; unsigned int type, host, hi, mid, lo; unsigned char *prom_name; } *hd = NULL, *sd = NULL; static int hdlen, sdlen; static void scan_walk_callback(int node) { int nextnode; char *prop; int len, disk; static int v0ctrl = 0; for (disk = 0; disk < hdlen + sdlen; disk++) { if (hd[disk].prom_node == node) { switch (hd[disk].type) { case SDSK_TYPE_IDE: for (nextnode = prom_getchild(node); nextnode; nextnode = prom_getsibling(nextnode)) { prop = prom_getproperty("name", &len); if (prop && len > 0 && (!strcmp (prop, "ata") || !strcmp (prop, "disk"))) break; } if (!nextnode) continue; if (prop[0] == 'a') sprintf (prop, "/ata@%x,0/cmdk@%x,0", hd[disk].hi, hd[disk].lo); else sprintf (prop, "/disk@%x,0", hd[disk].hi * 2 + hd[disk].lo); break; case SDSK_TYPE_SD: for (nextnode = prom_getchild(node); nextnode; nextnode = prom_getsibling(nextnode)) { prop = prom_getproperty("compatible", &len); if (prop && len > 0 && !strcmp (prop, "sd")) break; prop = prom_getproperty("name", &len); if (prop && len > 0 && (!strcmp (prop, "sd") || !strcmp (prop, "disk"))) break; } if (!nextnode || hd[disk].hi) continue; if (promvers) { char name[1024]; prop = prom_getproperty("name", &len); if (prop && len > 0) strcpy (name, prop); else strcpy (name, "sd"); if (!prop) prop = ((struct openpromio *)buf)->oprom_array; sprintf (prop, "/%s@%x,%x", name, hd[disk].mid, hd[disk].lo); } else { int i; for (i = 0; sd_targets[i]; i++) if (sd_targets[i] == '0' + hd[disk].mid) break; if (!sd_targets[i]) i = hd[disk].mid; sprintf (prop, "sd(%d,%d,", v0ctrl, i); } break; case SDSK_TYPE_PLN: prop = ((struct openpromio *)buf)->oprom_array; sprintf (prop, "/SUNW,pln@%x,%x/SUNW,ssd@%x,%x", hd[disk].lo & 0xf0000000, hd[disk].lo & 0xffffff, hd[disk].hi, hd[disk].mid); break; case SDSK_TYPE_FC: prop = ((struct openpromio *)buf)->oprom_array; sprintf (prop, "/sf@0,0/ssd@w%08x%08x,%x", hd[disk].hi, hd[disk].mid, hd[disk].lo); break; default: continue; } hd[disk].prom_name = malloc (strlen (prom_path) + strlen(prop) + 3); if (!hd[disk].prom_name) continue; if (promvers) strcpy (hd[disk].prom_name, prom_path); else hd[disk].prom_name[0] = '\0'; strcat (hd[disk].prom_name, prop); } } v0ctrl++; } static int scan_ide(void) { DIR * dir; char path[80]; char buffer[512]; int fd, i, disk; struct dirent * ent; int pci_bus, pci_devfn; if (access("/proc/ide", R_OK)) return 0; if (!(dir = opendir("/proc/ide"))) { return 1; } while ((ent = readdir(dir))) { if (ent->d_name[0] == 'h' && ent->d_name[1] == 'd' && ent->d_name[2] >= 'a' && ent->d_name[2] <= 'z' && ent->d_name[3] == '\0') { disk = ent->d_name[2] - 'a'; if (disk >= hdlen) { hd = (struct sdsk_disk *)realloc(hd, ((disk&~3)+4)*sizeof(struct sdsk_disk)); memset (hd + hdlen, 0, ((disk&~3)+4-hdlen)*sizeof(struct sdsk_disk)); hdlen = (disk&~3)+4; } for (i = (disk & ~3); i <= (disk | 3); i++) { if (hd[i].type) break; } if (i > (disk | 3)) { sprintf(path, "/proc/ide/%s", ent->d_name); if (readlink(path, buffer, 512) < 5) continue; if (strncmp(buffer, "ide", 3) || !isdigit(buffer[3]) || buffer[4] != '/') continue; buffer[4] = 0; sprintf(path, "/proc/ide/%s/config", buffer); if ((fd = open(path, O_RDONLY)) < 0) continue; i = read(fd, buffer, 50); close(fd); if (i < 50) continue; if (sscanf (buffer, "pci bus %x device %x ", &pci_bus, &pci_devfn) != 2) continue; hd[disk].prom_node = prom_pci2node (pci_bus, pci_devfn); } else hd[disk].prom_node = hd[i].prom_node; hd[disk].type = SDSK_TYPE_IDE; hd[disk].hi = (disk & 2) >> 1; hd[disk].lo = (disk & 1); } } closedir(dir); return 0; } static int scan_scsi(void) { FILE *f; DIR * dir, *dirhba; struct dirent * ent, *enthba; struct stat st; char * p, * q; char buf[512]; char path[128]; int disk = 0; int host, channel, id, lun; int prom_node, pci_bus, pci_devfn; if (access("/proc/scsi/scsi", R_OK)) { return 0; } f = fopen("/proc/scsi/scsi",