1 /* $NetBSD: openfirmio.c,v 1.5 2002/10/23 09:13:30 jdolecek Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratory. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. All advertising materials mentioning features or use of this software 25 * must display the following acknowledgement: 26 * This product includes software developed by the University of 27 * California, Berkeley and its contributors. 28 * 4. Neither the name of the University nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 * 44 * @(#)openfirm.c 8.1 (Berkeley) 6/11/93 45 */ 46 47 #include <sys/cdefs.h> 48 __KERNEL_RCSID(0, "$NetBSD: openfirmio.c,v 1.5 2002/10/23 09:13:30 jdolecek Exp $"); 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/errno.h> 53 #include <sys/fcntl.h> 54 #include <sys/ioctl.h> 55 #include <sys/malloc.h> 56 #include <sys/conf.h> 57 #include <sys/device.h> 58 #include <sys/event.h> 59 60 #include <dev/ofw/openfirm.h> 61 #include <dev/ofw/openfirmio.h> 62 63 static int lastnode; /* speed hack */ 64 65 static int openfirmcheckid (int, int); 66 static int openfirmgetstr (int, char *, char **); 67 68 void openfirmattach (int); 69 70 dev_type_ioctl(openfirmioctl); 71 72 const struct cdevsw openfirm_cdevsw = { 73 nullopen, nullclose, noread, nowrite, openfirmioctl, 74 nostop, notty, nopoll, nommap, nokqfilter, 75 }; 76 77 void 78 openfirmattach(int num) 79 { 80 /* nothing */ 81 } 82 83 /* 84 * Verify target ID is valid (exists in the OPENPROM tree), as 85 * listed from node ID sid forward. 86 */ 87 static int 88 openfirmcheckid(int sid, int tid) 89 { 90 91 for (; sid != 0; sid = OF_peer(sid)) 92 if (sid == tid || openfirmcheckid(OF_child(sid), tid)) 93 return (1); 94 95 return (0); 96 } 97 98 static int 99 openfirmgetstr(int len, char *user, char **cpp) 100 { 101 int error; 102 char *cp; 103 104 /* Reject obvious bogus requests */ 105 if ((u_int)len > (8 * 1024) - 1) 106 return (ENAMETOOLONG); 107 108 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK); 109 error = copyin(user, cp, len); 110 cp[len] = '\0'; 111 return (error); 112 } 113 114 int 115 openfirmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 116 { 117 struct ofiocdesc *of; 118 int node, len, ok, error, s; 119 char *name, *value; 120 121 if (cmd == OFIOCGETOPTNODE) { 122 s = splhigh(); 123 *(int *) data = OF_finddevice("/options"); 124 splx(s); 125 return (0); 126 } 127 128 /* Verify node id */ 129 of = (struct ofiocdesc *)data; 130 node = of->of_nodeid; 131 if (node != 0 && node != lastnode) { 132 /* Not an easy one, must search for it */ 133 s = splhigh(); 134 ok = openfirmcheckid(OF_peer(0), node); 135 splx(s); 136 if (!ok) 137 return (EINVAL); 138 lastnode = node; 139 } 140 141 name = value = NULL; 142 error = 0; 143 switch (cmd) { 144 145 case OFIOCGET: 146 if ((flags & FREAD) == 0) 147 return (EBADF); 148 if (node == 0) 149 return (EINVAL); 150 error = openfirmgetstr(of->of_namelen, of->of_name, &name); 151 if (error) 152 break; 153 s = splhigh(); 154 len = OF_getproplen(node, name); 155 splx(s); 156 if (len > of->of_buflen) { 157 error = ENOMEM; 158 break; 159 } 160 of->of_buflen = len; 161 /* -1 means no entry; 0 means no value */ 162 if (len <= 0) 163 break; 164 value = malloc(len, M_TEMP, M_WAITOK); 165 if (value == NULL) { 166 error = ENOMEM; 167 break; 168 } 169 s = splhigh(); 170 len = OF_getprop(node, name, (void *)value, len); 171 splx(s); 172 error = copyout(value, of->of_buf, len); 173 break; 174 175 #if 0 176 case OFIOCSET: 177 if ((flags & FWRITE) == 0) 178 return (EBADF); 179 if (node == 0) 180 return (EINVAL); 181 error = openfirmgetstr(of->of_namelen, of->of_name, &name); 182 if (error) 183 break; 184 error = openfirmgetstr(of->of_buflen, of->of_buf, &value); 185 if (error) 186 break; 187 s = splhigh(); 188 len = OF_setprop(node, name, value, of->of_buflen + 1); 189 splx(s); 190 if (len != of->of_buflen) 191 error = EINVAL; 192 break; 193 #endif 194 195 case OFIOCNEXTPROP: { 196 char newname[32]; 197 if ((flags & FREAD) == 0) 198 return (EBADF); 199 if (node == 0) 200 return (EINVAL); 201 if (of->of_namelen != 0) { 202 error = openfirmgetstr(of->of_namelen, of->of_name, 203 &name); 204 if (error) 205 break; 206 } 207 s = splhigh(); 208 ok = OF_nextprop(node, name, newname); 209 splx(s); 210 if (ok == 0) { 211 error = ENOENT; 212 break; 213 } 214 if (ok == -1) { 215 error = EINVAL; 216 break; 217 } 218 len = strlen(newname); 219 if (len > of->of_buflen) 220 len = of->of_buflen; 221 else 222 of->of_buflen = len; 223 error = copyout(newname, of->of_buf, len); 224 break; 225 } 226 227 case OFIOCGETNEXT: 228 if ((flags & FREAD) == 0) 229 return (EBADF); 230 s = splhigh(); 231 node = OF_peer(node); 232 splx(s); 233 *(int *)data = lastnode = node; 234 break; 235 236 case OFIOCGETCHILD: 237 if ((flags & FREAD) == 0) 238 return (EBADF); 239 if (node == 0) 240 return (EINVAL); 241 s = splhigh(); 242 node = OF_child(node); 243 splx(s); 244 *(int *)data = lastnode = node; 245 break; 246 247 case OFIOCFINDDEVICE: 248 if ((flags & FREAD) == 0) 249 return (EBADF); 250 error = openfirmgetstr(of->of_namelen, of->of_name, &name); 251 if (error) 252 break; 253 node = OF_finddevice(name); 254 if (node == 0 || node == -1) { 255 error = ENOENT; 256 break; 257 } 258 of->of_nodeid = lastnode = node; 259 break; 260 261 default: 262 return (ENOTTY); 263 } 264 265 if (name) 266 free(name, M_TEMP); 267 if (value) 268 free(value, M_TEMP); 269 270 return (error); 271 } 272