diff options
| author | David S. Miller <davem@davemloft.net> | 2006-06-26 02:19:30 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2006-06-26 02:19:30 -0400 |
| commit | 8e48aec714f1faf581949f23ae0e3d6e2317433b (patch) | |
| tree | 0b0c34ba577d64f54c2a29c5f748f93f2b1bf0d1 | |
| parent | 3d824a46b7210ea3b0a13ab0d0fbd7f6e2e91ddf (diff) | |
[OPENPROM]: Rewrite driver to use in-kernel device tree.
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/sbus/char/openprom.c | 593 |
1 files changed, 336 insertions, 257 deletions
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index cf5b476b5496..d7e4bb41bd79 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c | |||
| @@ -29,8 +29,6 @@ | |||
| 29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 30 | */ | 30 | */ |
| 31 | 31 | ||
| 32 | #define PROMLIB_INTERNAL | ||
| 33 | |||
| 34 | #include <linux/config.h> | 32 | #include <linux/config.h> |
| 35 | #include <linux/module.h> | 33 | #include <linux/module.h> |
| 36 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
| @@ -39,10 +37,10 @@ | |||
| 39 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
| 40 | #include <linux/string.h> | 38 | #include <linux/string.h> |
| 41 | #include <linux/miscdevice.h> | 39 | #include <linux/miscdevice.h> |
| 42 | #include <linux/smp_lock.h> | ||
| 43 | #include <linux/init.h> | 40 | #include <linux/init.h> |
| 44 | #include <linux/fs.h> | 41 | #include <linux/fs.h> |
| 45 | #include <asm/oplib.h> | 42 | #include <asm/oplib.h> |
| 43 | #include <asm/prom.h> | ||
| 46 | #include <asm/system.h> | 44 | #include <asm/system.h> |
| 47 | #include <asm/uaccess.h> | 45 | #include <asm/uaccess.h> |
| 48 | #include <asm/openpromio.h> | 46 | #include <asm/openpromio.h> |
| @@ -51,15 +49,20 @@ | |||
| 51 | #include <asm/pbm.h> | 49 | #include <asm/pbm.h> |
| 52 | #endif | 50 | #endif |
| 53 | 51 | ||
| 52 | MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)"); | ||
| 53 | MODULE_DESCRIPTION("OPENPROM Configuration Driver"); | ||
| 54 | MODULE_LICENSE("GPL"); | ||
| 55 | MODULE_VERSION("1.0"); | ||
| 56 | |||
| 54 | /* Private data kept by the driver for each descriptor. */ | 57 | /* Private data kept by the driver for each descriptor. */ |
| 55 | typedef struct openprom_private_data | 58 | typedef struct openprom_private_data |
| 56 | { | 59 | { |
| 57 | int current_node; /* Current node for SunOS ioctls. */ | 60 | struct device_node *current_node; /* Current node for SunOS ioctls. */ |
| 58 | int lastnode; /* Last valid node used by BSD ioctls. */ | 61 | struct device_node *lastnode; /* Last valid node used by BSD ioctls. */ |
| 59 | } DATA; | 62 | } DATA; |
| 60 | 63 | ||
| 61 | /* ID of the PROM node containing all of the EEPROM options. */ | 64 | /* ID of the PROM node containing all of the EEPROM options. */ |
| 62 | static int options_node = 0; | 65 | static struct device_node *options_node; |
| 63 | 66 | ||
| 64 | /* | 67 | /* |
| 65 | * Copy an openpromio structure into kernel space from user space. | 68 | * Copy an openpromio structure into kernel space from user space. |
| @@ -87,9 +90,8 @@ static int copyin(struct openpromio __user *info, struct openpromio **opp_p) | |||
| 87 | if (bufsize > OPROMMAXPARAM) | 90 | if (bufsize > OPROMMAXPARAM) |
| 88 | bufsize = OPROMMAXPARAM; | 91 | bufsize = OPROMMAXPARAM; |
| 89 | 92 | ||
| 90 | if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) | 93 | if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) |
| 91 | return -ENOMEM; | 94 | return -ENOMEM; |
| 92 | memset(*opp_p, 0, sizeof(int) + bufsize + 1); | ||
| 93 | 95 | ||
| 94 | if (copy_from_user(&(*opp_p)->oprom_array, | 96 | if (copy_from_user(&(*opp_p)->oprom_array, |
| 95 | &info->oprom_array, bufsize)) { | 97 | &info->oprom_array, bufsize)) { |
| @@ -107,10 +109,9 @@ static int getstrings(struct openpromio __user *info, struct openpromio **opp_p) | |||
| 107 | if (!info || !opp_p) | 109 | if (!info || !opp_p) |
| 108 | return -EFAULT; | 110 | return -EFAULT; |
| 109 | 111 | ||
| 110 | if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) | 112 | if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) |
| 111 | return -ENOMEM; | 113 | return -ENOMEM; |
| 112 | 114 | ||
| 113 | memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1); | ||
| 114 | (*opp_p)->oprom_size = 0; | 115 | (*opp_p)->oprom_size = 0; |
| 115 | 116 | ||
| 116 | n = bufsize = 0; | 117 | n = bufsize = 0; |
| @@ -140,16 +141,164 @@ static int copyout(void __user *info, struct openpromio *opp, int len) | |||
| 140 | return 0; | 141 | return 0; |
| 141 | } | 142 | } |
| 142 | 143 | ||
| 144 | static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) | ||
| 145 | { | ||
| 146 | void *pval; | ||
| 147 | int len; | ||
| 148 | |||
| 149 | pval = of_get_property(dp, op->oprom_array, &len); | ||
| 150 | if (!pval || len <= 0 || len > bufsize) | ||
| 151 | return copyout(argp, op, sizeof(int)); | ||
| 152 | |||
| 153 | memcpy(op->oprom_array, pval, len); | ||
| 154 | op->oprom_array[len] = '\0'; | ||
| 155 | op->oprom_size = len; | ||
| 156 | |||
| 157 | return copyout(argp, op, sizeof(int) + bufsize); | ||
| 158 | } | ||
| 159 | |||
| 160 | static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) | ||
| 161 | { | ||
| 162 | struct property *prop; | ||
| 163 | int len; | ||
| 164 | |||
| 165 | if (op->oprom_array[0] == '\0') { | ||
| 166 | prop = dp->properties; | ||
| 167 | if (!prop) | ||
| 168 | return copyout(argp, op, sizeof(int)); | ||
| 169 | len = strlen(prop->name); | ||
| 170 | } else { | ||
| 171 | prop = of_find_property(dp, op->oprom_array, NULL); | ||
| 172 | |||
| 173 | if (!prop || | ||
| 174 | !prop->next || | ||
| 175 | (len = strlen(prop->next->name)) + 1 > bufsize) | ||
| 176 | return copyout(argp, op, sizeof(int)); | ||
| 177 | |||
| 178 | prop = prop->next; | ||
| 179 | } | ||
| 180 | |||
| 181 | memcpy(op->oprom_array, prop->name, len); | ||
| 182 | op->oprom_array[len] = '\0'; | ||
| 183 | op->oprom_size = ++len; | ||
| 184 | |||
| 185 | return copyout(argp, op, sizeof(int) + bufsize); | ||
| 186 | } | ||
| 187 | |||
| 188 | static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize) | ||
| 189 | { | ||
| 190 | char *buf = op->oprom_array + strlen(op->oprom_array) + 1; | ||
| 191 | int len = op->oprom_array + bufsize - buf; | ||
| 192 | |||
| 193 | return of_set_property(options_node, op->oprom_array, buf, len); | ||
| 194 | } | ||
| 195 | |||
| 196 | static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) | ||
| 197 | { | ||
| 198 | phandle ph; | ||
| 199 | |||
| 200 | BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); | ||
| 201 | |||
| 202 | if (bufsize < sizeof(phandle)) | ||
| 203 | return -EINVAL; | ||
| 204 | |||
| 205 | ph = *((int *) op->oprom_array); | ||
| 206 | if (ph) { | ||
| 207 | dp = of_find_node_by_phandle(ph); | ||
| 208 | if (!dp) | ||
| 209 | return -EINVAL; | ||
| 210 | |||
| 211 | switch (cmd) { | ||
| 212 | case OPROMNEXT: | ||
| 213 | dp = dp->sibling; | ||
| 214 | break; | ||
| 215 | |||
| 216 | case OPROMCHILD: | ||
| 217 | dp = dp->child; | ||
| 218 | break; | ||
| 219 | |||
| 220 | case OPROMSETCUR: | ||
| 221 | default: | ||
| 222 | break; | ||
| 223 | }; | ||
| 224 | } else { | ||
| 225 | /* Sibling of node zero is the root node. */ | ||
| 226 | if (cmd != OPROMNEXT) | ||
| 227 | return -EINVAL; | ||
| 228 | |||
| 229 | dp = of_find_node_by_path("/"); | ||
| 230 | } | ||
| 231 | |||
| 232 | ph = 0; | ||
| 233 | if (dp) | ||
| 234 | ph = dp->node; | ||
| 235 | |||
| 236 | data->current_node = dp; | ||
| 237 | *((int *) op->oprom_array) = ph; | ||
| 238 | op->oprom_size = sizeof(phandle); | ||
| 239 | |||
| 240 | return copyout(argp, op, bufsize + sizeof(int)); | ||
| 241 | } | ||
| 242 | |||
| 243 | static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) | ||
| 244 | { | ||
| 245 | int err = -EINVAL; | ||
| 246 | |||
| 247 | if (bufsize >= 2*sizeof(int)) { | ||
| 248 | #ifdef CONFIG_PCI | ||
| 249 | struct pci_dev *pdev; | ||
| 250 | struct pcidev_cookie *pcp; | ||
| 251 | pdev = pci_find_slot (((int *) op->oprom_array)[0], | ||
| 252 | ((int *) op->oprom_array)[1]); | ||
| 253 | |||
| 254 | pcp = pdev->sysdata; | ||
| 255 | if (pcp != NULL) { | ||
| 256 | dp = pcp->prom_node; | ||
| 257 | data->current_node = dp; | ||
| 258 | *((int *)op->oprom_array) = dp->node; | ||
| 259 | op->oprom_size = sizeof(int); | ||
| 260 | err = copyout(argp, op, bufsize + sizeof(int)); | ||
| 261 | } | ||
| 262 | #endif | ||
| 263 | } | ||
| 264 | |||
| 265 | return err; | ||
| 266 | } | ||
| 267 | |||
| 268 | static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) | ||
| 269 | { | ||
| 270 | dp = of_find_node_by_path(op->oprom_array); | ||
| 271 | data->current_node = dp; | ||
| 272 | *((int *)op->oprom_array) = dp->node; | ||
| 273 | op->oprom_size = sizeof(int); | ||
| 274 | |||
| 275 | return copyout(argp, op, bufsize + sizeof(int)); | ||
| 276 | } | ||
| 277 | |||
| 278 | static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize) | ||
| 279 | { | ||
| 280 | char *buf = saved_command_line; | ||
| 281 | int len = strlen(buf); | ||
| 282 | |||
| 283 | if (len > bufsize) | ||
| 284 | return -EINVAL; | ||
| 285 | |||
| 286 | strcpy(op->oprom_array, buf); | ||
| 287 | op->oprom_size = len; | ||
| 288 | |||
| 289 | return copyout(argp, op, bufsize + sizeof(int)); | ||
| 290 | } | ||
| 291 | |||
| 143 | /* | 292 | /* |
| 144 | * SunOS and Solaris /dev/openprom ioctl calls. | 293 | * SunOS and Solaris /dev/openprom ioctl calls. |
| 145 | */ | 294 | */ |
| 146 | static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | 295 | static int openprom_sunos_ioctl(struct inode * inode, struct file * file, |
| 147 | unsigned int cmd, unsigned long arg, int node) | 296 | unsigned int cmd, unsigned long arg, |
| 297 | struct device_node *dp) | ||
| 148 | { | 298 | { |
| 149 | DATA *data = (DATA *) file->private_data; | 299 | DATA *data = file->private_data; |
| 150 | char buffer[OPROMMAXPARAM+1], *buf; | ||
| 151 | struct openpromio *opp; | 300 | struct openpromio *opp; |
| 152 | int bufsize, len, error = 0; | 301 | int bufsize, error = 0; |
| 153 | static int cnt; | 302 | static int cnt; |
| 154 | void __user *argp = (void __user *)arg; | 303 | void __user *argp = (void __user *)arg; |
| 155 | 304 | ||
| @@ -164,119 +313,35 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | |||
| 164 | switch (cmd) { | 313 | switch (cmd) { |
| 165 | case OPROMGETOPT: | 314 | case OPROMGETOPT: |
| 166 | case OPROMGETPROP: | 315 | case OPROMGETPROP: |
| 167 | len = prom_getproplen(node, opp->oprom_array); | 316 | error = opromgetprop(argp, dp, opp, bufsize); |
| 168 | |||
| 169 | if (len <= 0 || len > bufsize) { | ||
| 170 | error = copyout(argp, opp, sizeof(int)); | ||
| 171 | break; | ||
| 172 | } | ||
| 173 | |||
| 174 | len = prom_getproperty(node, opp->oprom_array, buffer, bufsize); | ||
| 175 | |||
| 176 | memcpy(opp->oprom_array, buffer, len); | ||
| 177 | opp->oprom_array[len] = '\0'; | ||
| 178 | opp->oprom_size = len; | ||
| 179 | |||
| 180 | error = copyout(argp, opp, sizeof(int) + bufsize); | ||
| 181 | break; | 317 | break; |
| 182 | 318 | ||
| 183 | case OPROMNXTOPT: | 319 | case OPROMNXTOPT: |
| 184 | case OPROMNXTPROP: | 320 | case OPROMNXTPROP: |
| 185 | buf = prom_nextprop(node, opp->oprom_array, buffer); | 321 | error = opromnxtprop(argp, dp, opp, bufsize); |
| 186 | |||
| 187 | len = strlen(buf); | ||
| 188 | if (len == 0 || len + 1 > bufsize) { | ||
| 189 | error = copyout(argp, opp, sizeof(int)); | ||
| 190 | break; | ||
| 191 | } | ||
| 192 | |||
| 193 | memcpy(opp->oprom_array, buf, len); | ||
| 194 | opp->oprom_array[len] = '\0'; | ||
| 195 | opp->oprom_size = ++len; | ||
| 196 | |||
| 197 | error = copyout(argp, opp, sizeof(int) + bufsize); | ||
| 198 | break; | 322 | break; |
| 199 | 323 | ||
| 200 | case OPROMSETOPT: | 324 | case OPROMSETOPT: |
| 201 | case OPROMSETOPT2: | 325 | case OPROMSETOPT2: |
| 202 | buf = opp->oprom_array + strlen(opp->oprom_array) + 1; | 326 | error = opromsetopt(dp, opp, bufsize); |
| 203 | len = opp->oprom_array + bufsize - buf; | ||
| 204 | |||
| 205 | error = prom_setprop(options_node, opp->oprom_array, | ||
| 206 | buf, len); | ||
| 207 | |||
| 208 | if (error < 0) | ||
| 209 | error = -EINVAL; | ||
| 210 | break; | 327 | break; |
| 211 | 328 | ||
| 212 | case OPROMNEXT: | 329 | case OPROMNEXT: |
| 213 | case OPROMCHILD: | 330 | case OPROMCHILD: |
| 214 | case OPROMSETCUR: | 331 | case OPROMSETCUR: |
| 215 | if (bufsize < sizeof(int)) { | 332 | error = opromnext(argp, cmd, dp, opp, bufsize, data); |
| 216 | error = -EINVAL; | ||
| 217 | break; | ||
| 218 | } | ||
| 219 | |||
| 220 | node = *((int *) opp->oprom_array); | ||
| 221 | |||
| 222 | switch (cmd) { | ||
| 223 | case OPROMNEXT: node = __prom_getsibling(node); break; | ||
| 224 | case OPROMCHILD: node = __prom_getchild(node); break; | ||
| 225 | case OPROMSETCUR: break; | ||
| 226 | } | ||
| 227 | |||
| 228 | data->current_node = node; | ||
| 229 | *((int *)opp->oprom_array) = node; | ||
| 230 | opp->oprom_size = sizeof(int); | ||
| 231 | |||
| 232 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
| 233 | break; | 333 | break; |
| 234 | 334 | ||
| 235 | case OPROMPCI2NODE: | 335 | case OPROMPCI2NODE: |
| 236 | error = -EINVAL; | 336 | error = oprompci2node(argp, dp, opp, bufsize, data); |
| 237 | |||
| 238 | if (bufsize >= 2*sizeof(int)) { | ||
| 239 | #ifdef CONFIG_PCI | ||
| 240 | struct pci_dev *pdev; | ||
| 241 | struct pcidev_cookie *pcp; | ||
| 242 | pdev = pci_find_slot (((int *) opp->oprom_array)[0], | ||
| 243 | ((int *) opp->oprom_array)[1]); | ||
| 244 | |||
| 245 | pcp = pdev->sysdata; | ||
| 246 | if (pcp != NULL) { | ||
| 247 | node = pcp->prom_node->node; | ||
| 248 | data->current_node = node; | ||
| 249 | *((int *)opp->oprom_array) = node; | ||
| 250 | opp->oprom_size = sizeof(int); | ||
| 251 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
| 252 | } | ||
| 253 | #endif | ||
| 254 | } | ||
| 255 | break; | 337 | break; |
| 256 | 338 | ||
| 257 | case OPROMPATH2NODE: | 339 | case OPROMPATH2NODE: |
| 258 | node = prom_finddevice(opp->oprom_array); | 340 | error = oprompath2node(argp, dp, opp, bufsize, data); |
| 259 | data->current_node = node; | ||
| 260 | *((int *)opp->oprom_array) = node; | ||
| 261 | opp->oprom_size = sizeof(int); | ||
| 262 | |||
| 263 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
| 264 | break; | 341 | break; |
| 265 | 342 | ||
| 266 | case OPROMGETBOOTARGS: | 343 | case OPROMGETBOOTARGS: |
| 267 | buf = saved_command_line; | 344 | error = opromgetbootargs(argp, opp, bufsize); |
| 268 | |||
| 269 | len = strlen(buf); | ||
| 270 | |||
| 271 | if (len > bufsize) { | ||
| 272 | error = -EINVAL; | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | |||
| 276 | strcpy(opp->oprom_array, buf); | ||
| 277 | opp->oprom_size = len; | ||
| 278 | |||
| 279 | error = copyout(argp, opp, bufsize + sizeof(int)); | ||
| 280 | break; | 345 | break; |
| 281 | 346 | ||
| 282 | case OPROMU2P: | 347 | case OPROMU2P: |
| @@ -297,25 +362,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | |||
| 297 | return error; | 362 | return error; |
| 298 | } | 363 | } |
| 299 | 364 | ||
| 300 | 365 | static struct device_node *get_node(phandle n, DATA *data) | |
| 301 | /* Return nonzero if a specific node is in the PROM device tree. */ | ||
| 302 | static int intree(int root, int node) | ||
| 303 | { | 366 | { |
| 304 | for (; root != 0; root = prom_getsibling(root)) | 367 | struct device_node *dp = of_find_node_by_phandle(n); |
| 305 | if (root == node || intree(prom_getchild(root),node)) | ||
| 306 | return 1; | ||
| 307 | return 0; | ||
| 308 | } | ||
| 309 | 368 | ||
| 310 | /* Return nonzero if a specific node is "valid". */ | 369 | if (dp) |
| 311 | static int goodnode(int n, DATA *data) | 370 | data->lastnode = dp; |
| 312 | { | 371 | |
| 313 | if (n == data->lastnode || n == prom_root_node || n == options_node) | 372 | return dp; |
| 314 | return 1; | ||
| 315 | if (n == 0 || n == -1 || !intree(prom_root_node,n)) | ||
| 316 | return 0; | ||
| 317 | data->lastnode = n; | ||
| 318 | return 1; | ||
| 319 | } | 373 | } |
| 320 | 374 | ||
| 321 | /* Copy in a whole string from userspace into kernelspace. */ | 375 | /* Copy in a whole string from userspace into kernelspace. */ |
| @@ -330,7 +384,7 @@ static int copyin_string(char __user *user, size_t len, char **ptr) | |||
| 330 | if (!tmp) | 384 | if (!tmp) |
| 331 | return -ENOMEM; | 385 | return -ENOMEM; |
| 332 | 386 | ||
| 333 | if(copy_from_user(tmp, user, len)) { | 387 | if (copy_from_user(tmp, user, len)) { |
| 334 | kfree(tmp); | 388 | kfree(tmp); |
| 335 | return -EFAULT; | 389 | return -EFAULT; |
| 336 | } | 390 | } |
| @@ -345,162 +399,187 @@ static int copyin_string(char __user *user, size_t len, char **ptr) | |||
| 345 | /* | 399 | /* |
| 346 | * NetBSD /dev/openprom ioctl calls. | 400 | * NetBSD /dev/openprom ioctl calls. |
| 347 | */ | 401 | */ |
| 348 | static int openprom_bsd_ioctl(struct inode * inode, struct file * file, | 402 | static int opiocget(void __user *argp, DATA *data) |
| 349 | unsigned int cmd, unsigned long arg) | ||
| 350 | { | 403 | { |
| 351 | DATA *data = (DATA *) file->private_data; | ||
| 352 | void __user *argp = (void __user *)arg; | ||
| 353 | struct opiocdesc op; | 404 | struct opiocdesc op; |
| 354 | int error, node, len; | 405 | struct device_node *dp; |
| 355 | char *str, *tmp; | 406 | char *str; |
| 356 | char buffer[64]; | 407 | void *pval; |
| 357 | static int cnt; | 408 | int err, len; |
| 358 | |||
| 359 | switch (cmd) { | ||
| 360 | case OPIOCGET: | ||
| 361 | if (copy_from_user(&op, argp, sizeof(op))) | ||
| 362 | return -EFAULT; | ||
| 363 | |||
| 364 | if (!goodnode(op.op_nodeid,data)) | ||
| 365 | return -EINVAL; | ||
| 366 | 409 | ||
| 367 | error = copyin_string(op.op_name, op.op_namelen, &str); | 410 | if (copy_from_user(&op, argp, sizeof(op))) |
| 368 | if (error) | 411 | return -EFAULT; |
| 369 | return error; | ||
| 370 | 412 | ||
| 371 | len = prom_getproplen(op.op_nodeid,str); | 413 | dp = get_node(op.op_nodeid, data); |
| 372 | 414 | ||
| 373 | if (len > op.op_buflen) { | 415 | err = copyin_string(op.op_name, op.op_namelen, &str); |
| 374 | kfree(str); | 416 | if (err) |
| 375 | return -ENOMEM; | 417 | return err; |
| 376 | } | ||
| 377 | 418 | ||
| 419 | pval = of_get_property(dp, str, &len); | ||
| 420 | err = 0; | ||
| 421 | if (!pval || len > op.op_buflen) { | ||
| 422 | err = -EINVAL; | ||
| 423 | } else { | ||
| 378 | op.op_buflen = len; | 424 | op.op_buflen = len; |
| 425 | if (copy_to_user(argp, &op, sizeof(op)) || | ||
| 426 | copy_to_user(op.op_buf, pval, len)) | ||
| 427 | err = -EFAULT; | ||
| 428 | } | ||
| 429 | kfree(str); | ||
| 379 | 430 | ||
| 380 | if (len <= 0) { | 431 | return err; |
| 381 | kfree(str); | 432 | } |
| 382 | /* Verified by the above copy_from_user */ | ||
| 383 | if (__copy_to_user(argp, &op, | ||
| 384 | sizeof(op))) | ||
| 385 | return -EFAULT; | ||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | 433 | ||
| 389 | tmp = kmalloc(len + 1, GFP_KERNEL); | 434 | static int opiocnextprop(void __user *argp, DATA *data) |
| 390 | if (!tmp) { | 435 | { |
| 391 | kfree(str); | 436 | struct opiocdesc op; |
| 392 | return -ENOMEM; | 437 | struct device_node *dp; |
| 393 | } | 438 | struct property *prop; |
| 439 | char *str; | ||
| 440 | int err, len; | ||
| 394 | 441 | ||
| 395 | cnt = prom_getproperty(op.op_nodeid, str, tmp, len); | 442 | if (copy_from_user(&op, argp, sizeof(op))) |
| 396 | if (cnt <= 0) { | 443 | return -EFAULT; |
| 397 | error = -EINVAL; | ||
| 398 | } else { | ||
| 399 | tmp[len] = '\0'; | ||
| 400 | 444 | ||
| 401 | if (__copy_to_user(argp, &op, sizeof(op)) != 0 || | 445 | dp = get_node(op.op_nodeid, data); |
| 402 | copy_to_user(op.op_buf, tmp, len) != 0) | 446 | if (!dp) |
| 403 | error = -EFAULT; | 447 | return -EINVAL; |
| 404 | } | ||
| 405 | 448 | ||
| 406 | kfree(tmp); | 449 | err = copyin_string(op.op_name, op.op_namelen, &str); |
| 407 | kfree(str); | 450 | if (err) |
| 451 | return err; | ||
| 408 | 452 | ||
| 409 | return error; | 453 | if (str[0] == '\0') { |
| 454 | prop = dp->properties; | ||
| 455 | } else { | ||
| 456 | prop = of_find_property(dp, str, NULL); | ||
| 457 | if (prop) | ||
| 458 | prop = prop->next; | ||
| 459 | } | ||
| 460 | kfree(str); | ||
| 410 | 461 | ||
| 411 | case OPIOCNEXTPROP: | 462 | if (!prop) |
| 412 | if (copy_from_user(&op, argp, sizeof(op))) | 463 | len = 0; |
| 413 | return -EFAULT; | 464 | else |
| 465 | len = prop->length; | ||
| 414 | 466 | ||
| 415 | if (!goodnode(op.op_nodeid,data)) | 467 | if (len > op.op_buflen) |
| 416 | return -EINVAL; | 468 | len = op.op_buflen; |
| 417 | 469 | ||
| 418 | error = copyin_string(op.op_name, op.op_namelen, &str); | 470 | if (copy_to_user(argp, &op, sizeof(op))) |
| 419 | if (error) | 471 | return -EFAULT; |
| 420 | return error; | ||
| 421 | 472 | ||
| 422 | tmp = prom_nextprop(op.op_nodeid,str,buffer); | 473 | if (len && |
| 474 | copy_to_user(op.op_buf, prop->value, len)) | ||
| 475 | return -EFAULT; | ||
| 423 | 476 | ||
| 424 | if (tmp) { | 477 | return 0; |
| 425 | len = strlen(tmp); | 478 | } |
| 426 | if (len > op.op_buflen) | ||
| 427 | len = op.op_buflen; | ||
| 428 | else | ||
| 429 | op.op_buflen = len; | ||
| 430 | } else { | ||
| 431 | len = op.op_buflen = 0; | ||
| 432 | } | ||
| 433 | 479 | ||
| 434 | if (!access_ok(VERIFY_WRITE, argp, sizeof(op))) { | 480 | static int opiocset(void __user *argp, DATA *data) |
| 435 | kfree(str); | 481 | { |
| 436 | return -EFAULT; | 482 | struct opiocdesc op; |
| 437 | } | 483 | struct device_node *dp; |
| 484 | char *str, *tmp; | ||
| 485 | int err; | ||
| 438 | 486 | ||
| 439 | if (!access_ok(VERIFY_WRITE, op.op_buf, len)) { | 487 | if (copy_from_user(&op, argp, sizeof(op))) |
| 440 | kfree(str); | 488 | return -EFAULT; |
| 441 | return -EFAULT; | 489 | |
| 442 | } | 490 | dp = get_node(op.op_nodeid, data); |
| 491 | if (!dp) | ||
| 492 | return -EINVAL; | ||
| 443 | 493 | ||
| 444 | error = __copy_to_user(argp, &op, sizeof(op)); | 494 | err = copyin_string(op.op_name, op.op_namelen, &str); |
| 445 | if (!error) error = __copy_to_user(op.op_buf, tmp, len); | 495 | if (err) |
| 496 | return err; | ||
| 446 | 497 | ||
| 498 | err = copyin_string(op.op_buf, op.op_buflen, &tmp); | ||
| 499 | if (err) { | ||
| 447 | kfree(str); | 500 | kfree(str); |
| 501 | return err; | ||
| 502 | } | ||
| 448 | 503 | ||
| 449 | return error; | 504 | err = of_set_property(dp, str, tmp, op.op_buflen); |
| 450 | 505 | ||
| 451 | case OPIOCSET: | 506 | kfree(str); |
| 452 | if (copy_from_user(&op, argp, sizeof(op))) | 507 | kfree(tmp); |
| 453 | return -EFAULT; | ||
| 454 | 508 | ||
| 455 | if (!goodnode(op.op_nodeid,data)) | 509 | return err; |
| 456 | return -EINVAL; | 510 | } |
| 457 | 511 | ||
| 458 | error = copyin_string(op.op_name, op.op_namelen, &str); | 512 | static int opiocgetnext(unsigned int cmd, void __user *argp) |
| 459 | if (error) | 513 | { |
| 460 | return error; | 514 | struct device_node *dp; |
| 515 | phandle nd; | ||
| 461 | 516 | ||
| 462 | error = copyin_string(op.op_buf, op.op_buflen, &tmp); | 517 | BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); |
| 463 | if (error) { | ||
| 464 | kfree(str); | ||
| 465 | return error; | ||
| 466 | } | ||
| 467 | 518 | ||
| 468 | len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1); | 519 | if (copy_from_user(&nd, argp, sizeof(phandle))) |
| 520 | return -EFAULT; | ||
| 469 | 521 | ||
| 470 | if (len != op.op_buflen) | 522 | if (nd == 0) { |
| 523 | if (cmd != OPIOCGETNEXT) | ||
| 471 | return -EINVAL; | 524 | return -EINVAL; |
| 525 | dp = of_find_node_by_path("/"); | ||
| 526 | } else { | ||
| 527 | dp = of_find_node_by_phandle(nd); | ||
| 528 | nd = 0; | ||
| 529 | if (dp) { | ||
| 530 | if (cmd == OPIOCGETNEXT) | ||
| 531 | dp = dp->sibling; | ||
| 532 | else | ||
| 533 | dp = dp->child; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | if (dp) | ||
| 537 | nd = dp->node; | ||
| 538 | if (copy_to_user(argp, &nd, sizeof(phandle))) | ||
| 539 | return -EFAULT; | ||
| 472 | 540 | ||
| 473 | kfree(str); | 541 | return 0; |
| 474 | kfree(tmp); | 542 | } |
| 475 | 543 | ||
| 476 | return 0; | 544 | static int openprom_bsd_ioctl(struct inode * inode, struct file * file, |
| 545 | unsigned int cmd, unsigned long arg) | ||
| 546 | { | ||
| 547 | DATA *data = (DATA *) file->private_data; | ||
| 548 | void __user *argp = (void __user *)arg; | ||
| 549 | int err; | ||
| 477 | 550 | ||
| 478 | case OPIOCGETOPTNODE: | 551 | switch (cmd) { |
| 479 | if (copy_to_user(argp, &options_node, sizeof(int))) | 552 | case OPIOCGET: |
| 480 | return -EFAULT; | 553 | err = opiocget(argp, data); |
| 481 | return 0; | 554 | break; |
| 482 | 555 | ||
| 483 | case OPIOCGETNEXT: | 556 | case OPIOCNEXTPROP: |
| 484 | case OPIOCGETCHILD: | 557 | err = opiocnextprop(argp, data); |
| 485 | if (copy_from_user(&node, argp, sizeof(int))) | 558 | break; |
| 486 | return -EFAULT; | ||
| 487 | 559 | ||
| 488 | if (cmd == OPIOCGETNEXT) | 560 | case OPIOCSET: |
| 489 | node = __prom_getsibling(node); | 561 | err = opiocset(argp, data); |
| 490 | else | 562 | break; |
| 491 | node = __prom_getchild(node); | 563 | |
| 564 | case OPIOCGETOPTNODE: | ||
| 565 | BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); | ||
| 492 | 566 | ||
| 493 | if (__copy_to_user(argp, &node, sizeof(int))) | 567 | if (copy_to_user(argp, &options_node->node, sizeof(phandle))) |
| 494 | return -EFAULT; | 568 | return -EFAULT; |
| 495 | 569 | ||
| 496 | return 0; | 570 | return 0; |
| 497 | 571 | ||
| 572 | case OPIOCGETNEXT: | ||
| 573 | case OPIOCGETCHILD: | ||
| 574 | err = opiocgetnext(cmd, argp); | ||
| 575 | break; | ||
| 576 | |||
| 498 | default: | 577 | default: |
| 499 | if (cnt++ < 10) | ||
| 500 | printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd); | ||
| 501 | return -EINVAL; | 578 | return -EINVAL; |
| 502 | 579 | ||
| 503 | } | 580 | }; |
| 581 | |||
| 582 | return err; | ||
| 504 | } | 583 | } |
| 505 | 584 | ||
| 506 | 585 | ||
| @@ -511,7 +590,6 @@ static int openprom_ioctl(struct inode * inode, struct file * file, | |||
| 511 | unsigned int cmd, unsigned long arg) | 590 | unsigned int cmd, unsigned long arg) |
| 512 | { | 591 | { |
| 513 | DATA *data = (DATA *) file->private_data; | 592 | DATA *data = (DATA *) file->private_data; |
| 514 | static int cnt; | ||
| 515 | 593 | ||
| 516 | switch (cmd) { | 594 | switch (cmd) { |
| 517 | case OPROMGETOPT: | 595 | case OPROMGETOPT: |
| @@ -563,10 +641,8 @@ static int openprom_ioctl(struct inode * inode, struct file * file, | |||
| 563 | return openprom_bsd_ioctl(inode,file,cmd,arg); | 641 | return openprom_bsd_ioctl(inode,file,cmd,arg); |
| 564 | 642 | ||
| 565 | default: | 643 | default: |
| 566 | if (cnt++ < 10) | ||
| 567 | printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); | ||
| 568 | return -EINVAL; | 644 | return -EINVAL; |
| 569 | } | 645 | }; |
| 570 | } | 646 | } |
| 571 | 647 | ||
| 572 | static long openprom_compat_ioctl(struct file *file, unsigned int cmd, | 648 | static long openprom_compat_ioctl(struct file *file, unsigned int cmd, |
| @@ -594,9 +670,7 @@ static long openprom_compat_ioctl(struct file *file, unsigned int cmd, | |||
| 594 | case OPROMSETCUR: | 670 | case OPROMSETCUR: |
| 595 | case OPROMPCI2NODE: | 671 | case OPROMPCI2NODE: |
| 596 | case OPROMPATH2NODE: | 672 | case OPROMPATH2NODE: |
| 597 | lock_kernel(); | ||
| 598 | rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg); | 673 | rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg); |
| 599 | lock_kernel(); | ||
| 600 | break; | 674 | break; |
| 601 | } | 675 | } |
| 602 | 676 | ||
| @@ -607,13 +681,13 @@ static int openprom_open(struct inode * inode, struct file * file) | |||
| 607 | { | 681 | { |
| 608 | DATA *data; | 682 | DATA *data; |
| 609 | 683 | ||
| 610 | data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL); | 684 | data = kmalloc(sizeof(DATA), GFP_KERNEL); |
| 611 | if (!data) | 685 | if (!data) |
| 612 | return -ENOMEM; | 686 | return -ENOMEM; |
| 613 | 687 | ||
| 614 | data->current_node = prom_root_node; | 688 | data->current_node = of_find_node_by_path("/"); |
| 615 | data->lastnode = prom_root_node; | 689 | data->lastnode = data->current_node; |
| 616 | file->private_data = (void *)data; | 690 | file->private_data = (void *) data; |
| 617 | 691 | ||
| 618 | return 0; | 692 | return 0; |
| 619 | } | 693 | } |
| @@ -634,24 +708,30 @@ static struct file_operations openprom_fops = { | |||
| 634 | }; | 708 | }; |
| 635 | 709 | ||
| 636 | static struct miscdevice openprom_dev = { | 710 | static struct miscdevice openprom_dev = { |
| 637 | SUN_OPENPROM_MINOR, "openprom", &openprom_fops | 711 | .minor = SUN_OPENPROM_MINOR, |
| 712 | .name = "openprom", | ||
| 713 | .fops = &openprom_fops, | ||
| 638 | }; | 714 | }; |
| 639 | 715 | ||
| 640 | static int __init openprom_init(void) | 716 | static int __init openprom_init(void) |
| 641 | { | 717 | { |
| 642 | int error; | 718 | struct device_node *dp; |
| 719 | int err; | ||
| 643 | 720 | ||
| 644 | error = misc_register(&openprom_dev); | 721 | err = misc_register(&openprom_dev); |
| 645 | if (error) { | 722 | if (err) |
| 646 | printk(KERN_ERR "openprom: unable to get misc minor\n"); | 723 | return err; |
| 647 | return error; | ||
| 648 | } | ||
| 649 | 724 | ||
| 650 | options_node = prom_getchild(prom_root_node); | 725 | dp = of_find_node_by_path("/"); |
| 651 | options_node = prom_searchsiblings(options_node,"options"); | 726 | dp = dp->child; |
| 727 | while (dp) { | ||
| 728 | if (!strcmp(dp->name, "options")) | ||
| 729 | break; | ||
| 730 | dp = dp->sibling; | ||
| 731 | } | ||
| 732 | options_node = dp; | ||
| 652 | 733 | ||
| 653 | if (options_node == 0 || options_node == -1) { | 734 | if (!options_node) { |
| 654 | printk(KERN_ERR "openprom: unable to find options node\n"); | ||
| 655 | misc_deregister(&openprom_dev); | 735 | misc_deregister(&openprom_dev); |
| 656 | return -EIO; | 736 | return -EIO; |
| 657 | } | 737 | } |
| @@ -666,4 +746,3 @@ static void __exit openprom_cleanup(void) | |||
| 666 | 746 | ||
| 667 | module_init(openprom_init); | 747 | module_init(openprom_init); |
| 668 | module_exit(openprom_cleanup); | 748 | module_exit(openprom_cleanup); |
| 669 | MODULE_LICENSE("GPL"); | ||
