2005-04-17 02:20:36 +04:00
/*
* Linux / SPARC PROM Configuration Driver
* Copyright ( C ) 1996 Thomas K . Dyas ( tdyas @ noc . rutgers . edu )
* Copyright ( C ) 1996 Eddie C . Dost ( ecd @ skynet . be )
*
* This character device driver allows user programs to access the
* PROM device tree . It is compatible with the SunOS / dev / openprom
* driver and the NetBSD / dev / openprom driver . The SunOS eeprom
* utility works without any modifications .
*
* The driver uses a minor number under the misc device major . The
* file read / write mode determines the type of access to the PROM .
* Interrupts are disabled whenever the driver calls into the PROM for
* sanity ' s sake .
*/
/* 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 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
2010-07-21 10:36:39 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/string.h>
# include <linux/miscdevice.h>
# include <linux/init.h>
# include <linux/fs.h>
# include <asm/oplib.h>
2006-06-26 10:19:30 +04:00
# include <asm/prom.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/openpromio.h>
# ifdef CONFIG_PCI
# include <linux/pci.h>
# endif
2006-06-26 10:19:30 +04:00
MODULE_AUTHOR ( " Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be) " ) ;
MODULE_DESCRIPTION ( " OPENPROM Configuration Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( " 1.0 " ) ;
2009-03-14 00:30:08 +03:00
MODULE_ALIAS_MISCDEV ( SUN_OPENPROM_MINOR ) ;
2006-06-26 10:19:30 +04:00
2005-04-17 02:20:36 +04:00
/* Private data kept by the driver for each descriptor. */
typedef struct openprom_private_data
{
2006-06-26 10:19:30 +04:00
struct device_node * current_node ; /* Current node for SunOS ioctls. */
struct device_node * lastnode ; /* Last valid node used by BSD ioctls. */
2005-04-17 02:20:36 +04:00
} DATA ;
/* ID of the PROM node containing all of the EEPROM options. */
2010-07-21 10:36:39 +04:00
static DEFINE_MUTEX ( openprom_mutex ) ;
2006-06-26 10:19:30 +04:00
static struct device_node * options_node ;
2005-04-17 02:20:36 +04:00
/*
* Copy an openpromio structure into kernel space from user space .
* This routine does error checking to make sure that all memory
* accesses are within bounds . A pointer to the allocated openpromio
* structure will be placed in " *opp_p " . Return value is the length
* of the user supplied buffer .
*/
static int copyin ( struct openpromio __user * info , struct openpromio * * opp_p )
{
unsigned int bufsize ;
if ( ! info | | ! opp_p )
return - EFAULT ;
if ( get_user ( bufsize , & info - > oprom_size ) )
return - EFAULT ;
if ( bufsize = = 0 )
return - EINVAL ;
/* If the bufsize is too large, just limit it.
* Fix from Jason Rappleye .
*/
if ( bufsize > OPROMMAXPARAM )
bufsize = OPROMMAXPARAM ;
2006-06-26 10:19:30 +04:00
if ( ! ( * opp_p = kzalloc ( sizeof ( int ) + bufsize + 1 , GFP_KERNEL ) ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
if ( copy_from_user ( & ( * opp_p ) - > oprom_array ,
& info - > oprom_array , bufsize ) ) {
kfree ( * opp_p ) ;
return - EFAULT ;
}
return bufsize ;
}
static int getstrings ( struct openpromio __user * info , struct openpromio * * opp_p )
{
int n , bufsize ;
char c ;
if ( ! info | | ! opp_p )
return - EFAULT ;
2006-06-26 10:19:30 +04:00
if ( ! ( * opp_p = kzalloc ( sizeof ( int ) + OPROMMAXPARAM + 1 , GFP_KERNEL ) ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
( * opp_p ) - > oprom_size = 0 ;
n = bufsize = 0 ;
while ( ( n < 2 ) & & ( bufsize < OPROMMAXPARAM ) ) {
if ( get_user ( c , & info - > oprom_array [ bufsize ] ) ) {
kfree ( * opp_p ) ;
return - EFAULT ;
}
if ( c = = ' \0 ' )
n + + ;
( * opp_p ) - > oprom_array [ bufsize + + ] = c ;
}
if ( ! n ) {
kfree ( * opp_p ) ;
return - EINVAL ;
}
return bufsize ;
}
/*
* Copy an openpromio structure in kernel space back to user space .
*/
static int copyout ( void __user * info , struct openpromio * opp , int len )
{
if ( copy_to_user ( info , opp , len ) )
return - EFAULT ;
return 0 ;
}
2006-06-26 10:19:30 +04:00
static int opromgetprop ( void __user * argp , struct device_node * dp , struct openpromio * op , int bufsize )
{
2007-03-29 11:49:54 +04:00
const void * pval ;
2006-06-26 10:19:30 +04:00
int len ;
2006-09-18 12:47:13 +04:00
if ( ! dp | |
! ( pval = of_get_property ( dp , op - > oprom_array , & len ) ) | |
len < = 0 | | len > bufsize )
2006-06-26 10:19:30 +04:00
return copyout ( argp , op , sizeof ( int ) ) ;
memcpy ( op - > oprom_array , pval , len ) ;
op - > oprom_array [ len ] = ' \0 ' ;
op - > oprom_size = len ;
return copyout ( argp , op , sizeof ( int ) + bufsize ) ;
}
static int opromnxtprop ( void __user * argp , struct device_node * dp , struct openpromio * op , int bufsize )
{
struct property * prop ;
int len ;
2006-09-18 12:47:13 +04:00
if ( ! dp )
return copyout ( argp , op , sizeof ( int ) ) ;
2006-06-26 10:19:30 +04:00
if ( op - > oprom_array [ 0 ] = = ' \0 ' ) {
prop = dp - > properties ;
if ( ! prop )
return copyout ( argp , op , sizeof ( int ) ) ;
len = strlen ( prop - > name ) ;
} else {
prop = of_find_property ( dp , op - > oprom_array , NULL ) ;
if ( ! prop | |
! prop - > next | |
( len = strlen ( prop - > next - > name ) ) + 1 > bufsize )
return copyout ( argp , op , sizeof ( int ) ) ;
prop = prop - > next ;
}
memcpy ( op - > oprom_array , prop - > name , len ) ;
op - > oprom_array [ len ] = ' \0 ' ;
op - > oprom_size = + + len ;
return copyout ( argp , op , sizeof ( int ) + bufsize ) ;
}
static int opromsetopt ( struct device_node * dp , struct openpromio * op , int bufsize )
{
char * buf = op - > oprom_array + strlen ( op - > oprom_array ) + 1 ;
int len = op - > oprom_array + bufsize - buf ;
return of_set_property ( options_node , op - > oprom_array , buf , len ) ;
}
static int opromnext ( void __user * argp , unsigned int cmd , struct device_node * dp , struct openpromio * op , int bufsize , DATA * data )
{
phandle ph ;
BUILD_BUG_ON ( sizeof ( phandle ) ! = sizeof ( int ) ) ;
if ( bufsize < sizeof ( phandle ) )
return - EINVAL ;
ph = * ( ( int * ) op - > oprom_array ) ;
if ( ph ) {
dp = of_find_node_by_phandle ( ph ) ;
if ( ! dp )
return - EINVAL ;
switch ( cmd ) {
case OPROMNEXT :
dp = dp - > sibling ;
break ;
case OPROMCHILD :
dp = dp - > child ;
break ;
case OPROMSETCUR :
default :
break ;
2012-09-12 11:03:12 +04:00
}
2006-06-26 10:19:30 +04:00
} else {
/* Sibling of node zero is the root node. */
if ( cmd ! = OPROMNEXT )
return - EINVAL ;
dp = of_find_node_by_path ( " / " ) ;
}
ph = 0 ;
if ( dp )
2010-01-29 00:06:53 +03:00
ph = dp - > phandle ;
2006-06-26 10:19:30 +04:00
data - > current_node = dp ;
* ( ( int * ) op - > oprom_array ) = ph ;
op - > oprom_size = sizeof ( phandle ) ;
return copyout ( argp , op , bufsize + sizeof ( int ) ) ;
}
static int oprompci2node ( void __user * argp , struct device_node * dp , struct openpromio * op , int bufsize , DATA * data )
{
int err = - EINVAL ;
if ( bufsize > = 2 * sizeof ( int ) ) {
# ifdef CONFIG_PCI
struct pci_dev * pdev ;
2007-04-26 03:01:51 +04:00
struct device_node * dp ;
2007-04-24 09:50:53 +04:00
pdev = pci_get_bus_and_slot ( ( ( int * ) op - > oprom_array ) [ 0 ] ,
2006-06-26 10:19:30 +04:00
( ( int * ) op - > oprom_array ) [ 1 ] ) ;
2007-04-26 03:01:51 +04:00
dp = pci_device_to_OF_node ( pdev ) ;
data - > current_node = dp ;
2010-01-29 00:06:53 +03:00
* ( ( int * ) op - > oprom_array ) = dp - > phandle ;
2007-04-26 03:01:51 +04:00
op - > oprom_size = sizeof ( int ) ;
err = copyout ( argp , op , bufsize + sizeof ( int ) ) ;
2007-04-24 09:50:53 +04:00
pci_dev_put ( pdev ) ;
2006-06-26 10:19:30 +04:00
# endif
}
return err ;
}
static int oprompath2node ( void __user * argp , struct device_node * dp , struct openpromio * op , int bufsize , DATA * data )
{
2006-09-18 12:47:13 +04:00
phandle ph = 0 ;
2006-06-26 10:19:30 +04:00
dp = of_find_node_by_path ( op - > oprom_array ) ;
2006-09-18 12:47:13 +04:00
if ( dp )
2010-01-29 00:06:53 +03:00
ph = dp - > phandle ;
2006-06-26 10:19:30 +04:00
data - > current_node = dp ;
2006-09-18 12:47:13 +04:00
* ( ( int * ) op - > oprom_array ) = ph ;
2006-06-26 10:19:30 +04:00
op - > oprom_size = sizeof ( int ) ;
return copyout ( argp , op , bufsize + sizeof ( int ) ) ;
}
static int opromgetbootargs ( void __user * argp , struct openpromio * op , int bufsize )
{
char * buf = saved_command_line ;
int len = strlen ( buf ) ;
if ( len > bufsize )
return - EINVAL ;
strcpy ( op - > oprom_array , buf ) ;
op - > oprom_size = len ;
return copyout ( argp , op , bufsize + sizeof ( int ) ) ;
}
2005-04-17 02:20:36 +04:00
/*
* SunOS and Solaris / dev / openprom ioctl calls .
*/
2010-04-27 02:24:05 +04:00
static long openprom_sunos_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg ,
struct device_node * dp )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:30 +04:00
DATA * data = file - > private_data ;
2009-06-16 14:46:54 +04:00
struct openpromio * opp = NULL ;
2006-06-26 10:19:30 +04:00
int bufsize , error = 0 ;
2005-04-17 02:20:36 +04:00
static int cnt ;
void __user * argp = ( void __user * ) arg ;
if ( cmd = = OPROMSETOPT )
bufsize = getstrings ( argp , & opp ) ;
else
bufsize = copyin ( argp , & opp ) ;
if ( bufsize < 0 )
return bufsize ;
2010-07-21 10:36:39 +04:00
mutex_lock ( & openprom_mutex ) ;
2010-04-27 02:24:05 +04:00
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case OPROMGETOPT :
case OPROMGETPROP :
2006-06-26 10:19:30 +04:00
error = opromgetprop ( argp , dp , opp , bufsize ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMNXTOPT :
case OPROMNXTPROP :
2006-06-26 10:19:30 +04:00
error = opromnxtprop ( argp , dp , opp , bufsize ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMSETOPT :
case OPROMSETOPT2 :
2006-06-26 10:19:30 +04:00
error = opromsetopt ( dp , opp , bufsize ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMNEXT :
case OPROMCHILD :
case OPROMSETCUR :
2006-06-26 10:19:30 +04:00
error = opromnext ( argp , cmd , dp , opp , bufsize , data ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMPCI2NODE :
2006-06-26 10:19:30 +04:00
error = oprompci2node ( argp , dp , opp , bufsize , data ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMPATH2NODE :
2006-06-26 10:19:30 +04:00
error = oprompath2node ( argp , dp , opp , bufsize , data ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMGETBOOTARGS :
2006-06-26 10:19:30 +04:00
error = opromgetbootargs ( argp , opp , bufsize ) ;
2005-04-17 02:20:36 +04:00
break ;
case OPROMU2P :
case OPROMGETCONS :
case OPROMGETFBNAME :
if ( cnt + + < 10 )
printk ( KERN_INFO " openprom_sunos_ioctl: unimplemented ioctl \n " ) ;
error = - EINVAL ;
break ;
default :
if ( cnt + + < 10 )
printk ( KERN_INFO " openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX \n " , cmd , arg ) ;
error = - EINVAL ;
break ;
}
kfree ( opp ) ;
2010-07-21 10:36:39 +04:00
mutex_unlock ( & openprom_mutex ) ;
2010-04-27 02:24:05 +04:00
2005-04-17 02:20:36 +04:00
return error ;
}
2006-06-26 10:19:30 +04:00
static struct device_node * get_node ( phandle n , DATA * data )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:30 +04:00
struct device_node * dp = of_find_node_by_phandle ( n ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( dp )
data - > lastnode = dp ;
return dp ;
2005-04-17 02:20:36 +04:00
}
/* Copy in a whole string from userspace into kernelspace. */
static int copyin_string ( char __user * user , size_t len , char * * ptr )
{
char * tmp ;
if ( ( ssize_t ) len < 0 | | ( ssize_t ) ( len + 1 ) < 0 )
return - EINVAL ;
tmp = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! tmp )
return - ENOMEM ;
2006-06-26 10:19:30 +04:00
if ( copy_from_user ( tmp , user , len ) ) {
2005-04-17 02:20:36 +04:00
kfree ( tmp ) ;
return - EFAULT ;
}
tmp [ len ] = ' \0 ' ;
* ptr = tmp ;
return 0 ;
}
/*
* NetBSD / dev / openprom ioctl calls .
*/
2006-06-26 10:19:30 +04:00
static int opiocget ( void __user * argp , DATA * data )
2005-04-17 02:20:36 +04:00
{
struct opiocdesc op ;
2006-06-26 10:19:30 +04:00
struct device_node * dp ;
char * str ;
2007-03-29 11:49:54 +04:00
const void * pval ;
2006-06-26 10:19:30 +04:00
int err , len ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( copy_from_user ( & op , argp , sizeof ( op ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
dp = get_node ( op . op_nodeid , data ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
err = copyin_string ( op . op_name , op . op_namelen , & str ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
pval = of_get_property ( dp , str , & len ) ;
err = 0 ;
if ( ! pval | | len > op . op_buflen ) {
err = - EINVAL ;
} else {
2005-04-17 02:20:36 +04:00
op . op_buflen = len ;
2006-06-26 10:19:30 +04:00
if ( copy_to_user ( argp , & op , sizeof ( op ) ) | |
copy_to_user ( op . op_buf , pval , len ) )
err = - EFAULT ;
}
kfree ( str ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
return err ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
static int opiocnextprop ( void __user * argp , DATA * data )
{
struct opiocdesc op ;
struct device_node * dp ;
struct property * prop ;
char * str ;
int err , len ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( copy_from_user ( & op , argp , sizeof ( op ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
dp = get_node ( op . op_nodeid , data ) ;
if ( ! dp )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
err = copyin_string ( op . op_name , op . op_namelen , & str ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( str [ 0 ] = = ' \0 ' ) {
prop = dp - > properties ;
} else {
prop = of_find_property ( dp , str , NULL ) ;
if ( prop )
prop = prop - > next ;
}
kfree ( str ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( ! prop )
len = 0 ;
else
len = prop - > length ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( len > op . op_buflen )
len = op . op_buflen ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( copy_to_user ( argp , & op , sizeof ( op ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( len & &
copy_to_user ( op . op_buf , prop - > value , len ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
static int opiocset ( void __user * argp , DATA * data )
{
struct opiocdesc op ;
struct device_node * dp ;
char * str , * tmp ;
int err ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( copy_from_user ( & op , argp , sizeof ( op ) ) )
return - EFAULT ;
dp = get_node ( op . op_nodeid , data ) ;
if ( ! dp )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
err = copyin_string ( op . op_name , op . op_namelen , & str ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
err = copyin_string ( op . op_buf , op . op_buflen , & tmp ) ;
if ( err ) {
2005-04-17 02:20:36 +04:00
kfree ( str ) ;
2006-06-26 10:19:30 +04:00
return err ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
err = of_set_property ( dp , str , tmp , op . op_buflen ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
kfree ( str ) ;
kfree ( tmp ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
return err ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
static int opiocgetnext ( unsigned int cmd , void __user * argp )
{
struct device_node * dp ;
phandle nd ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
BUILD_BUG_ON ( sizeof ( phandle ) ! = sizeof ( int ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( copy_from_user ( & nd , argp , sizeof ( phandle ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( nd = = 0 ) {
if ( cmd ! = OPIOCGETNEXT )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-06-26 10:19:30 +04:00
dp = of_find_node_by_path ( " / " ) ;
} else {
dp = of_find_node_by_phandle ( nd ) ;
nd = 0 ;
if ( dp ) {
if ( cmd = = OPIOCGETNEXT )
dp = dp - > sibling ;
else
dp = dp - > child ;
}
}
if ( dp )
2010-01-29 00:06:53 +03:00
nd = dp - > phandle ;
2006-06-26 10:19:30 +04:00
if ( copy_to_user ( argp , & nd , sizeof ( phandle ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2010-04-27 02:24:05 +04:00
static int openprom_bsd_ioctl ( struct file * file ,
2006-06-26 10:19:30 +04:00
unsigned int cmd , unsigned long arg )
{
2010-07-13 08:16:04 +04:00
DATA * data = file - > private_data ;
2006-06-26 10:19:30 +04:00
void __user * argp = ( void __user * ) arg ;
int err ;
2005-04-17 02:20:36 +04:00
2010-07-21 10:36:39 +04:00
mutex_lock ( & openprom_mutex ) ;
2006-06-26 10:19:30 +04:00
switch ( cmd ) {
case OPIOCGET :
err = opiocget ( argp , data ) ;
break ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
case OPIOCNEXTPROP :
err = opiocnextprop ( argp , data ) ;
break ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
case OPIOCSET :
err = opiocset ( argp , data ) ;
break ;
case OPIOCGETOPTNODE :
BUILD_BUG_ON ( sizeof ( phandle ) ! = sizeof ( int ) ) ;
2005-04-17 02:20:36 +04:00
2010-04-27 02:24:05 +04:00
err = 0 ;
2010-01-29 00:06:53 +03:00
if ( copy_to_user ( argp , & options_node - > phandle , sizeof ( phandle ) ) )
2010-04-27 02:24:05 +04:00
err = - EFAULT ;
break ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
case OPIOCGETNEXT :
case OPIOCGETCHILD :
err = opiocgetnext ( cmd , argp ) ;
break ;
2005-04-17 02:20:36 +04:00
default :
2010-04-27 02:24:05 +04:00
err = - EINVAL ;
break ;
2012-09-12 11:03:12 +04:00
}
2010-07-21 10:36:39 +04:00
mutex_unlock ( & openprom_mutex ) ;
2006-06-26 10:19:30 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
/*
* Handoff control to the correct ioctl handler .
*/
2010-04-27 02:24:05 +04:00
static long openprom_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2010-07-13 08:16:04 +04:00
DATA * data = file - > private_data ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case OPROMGETOPT :
case OPROMNXTOPT :
if ( ( file - > f_mode & FMODE_READ ) = = 0 )
return - EPERM ;
2010-04-27 02:24:05 +04:00
return openprom_sunos_ioctl ( file , cmd , arg ,
2005-04-17 02:20:36 +04:00
options_node ) ;
case OPROMSETOPT :
case OPROMSETOPT2 :
if ( ( file - > f_mode & FMODE_WRITE ) = = 0 )
return - EPERM ;
2010-04-27 02:24:05 +04:00
return openprom_sunos_ioctl ( file , cmd , arg ,
2005-04-17 02:20:36 +04:00
options_node ) ;
case OPROMNEXT :
case OPROMCHILD :
case OPROMGETPROP :
case OPROMNXTPROP :
if ( ( file - > f_mode & FMODE_READ ) = = 0 )
return - EPERM ;
2010-04-27 02:24:05 +04:00
return openprom_sunos_ioctl ( file , cmd , arg ,
2005-04-17 02:20:36 +04:00
data - > current_node ) ;
case OPROMU2P :
case OPROMGETCONS :
case OPROMGETFBNAME :
case OPROMGETBOOTARGS :
case OPROMSETCUR :
case OPROMPCI2NODE :
case OPROMPATH2NODE :
if ( ( file - > f_mode & FMODE_READ ) = = 0 )
return - EPERM ;
2010-04-27 02:24:05 +04:00
return openprom_sunos_ioctl ( file , cmd , arg , NULL ) ;
2005-04-17 02:20:36 +04:00
case OPIOCGET :
case OPIOCNEXTPROP :
case OPIOCGETOPTNODE :
case OPIOCGETNEXT :
case OPIOCGETCHILD :
if ( ( file - > f_mode & FMODE_READ ) = = 0 )
return - EBADF ;
2010-04-27 02:24:05 +04:00
return openprom_bsd_ioctl ( file , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
case OPIOCSET :
if ( ( file - > f_mode & FMODE_WRITE ) = = 0 )
return - EBADF ;
2010-04-27 02:24:05 +04:00
return openprom_bsd_ioctl ( file , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
default :
return - EINVAL ;
2006-06-26 10:19:30 +04:00
} ;
2005-04-17 02:20:36 +04:00
}
2005-11-08 01:12:47 +03:00
static long openprom_compat_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
long rval = - ENOTTY ;
/*
* SunOS / Solaris only , the NetBSD one ' s have embedded pointers in
* the arg which we ' d need to clean up . . .
*/
switch ( cmd ) {
case OPROMGETOPT :
case OPROMSETOPT :
case OPROMNXTOPT :
case OPROMSETOPT2 :
case OPROMNEXT :
case OPROMCHILD :
case OPROMGETPROP :
case OPROMNXTPROP :
case OPROMU2P :
case OPROMGETCONS :
case OPROMGETFBNAME :
case OPROMGETBOOTARGS :
case OPROMSETCUR :
case OPROMPCI2NODE :
case OPROMPATH2NODE :
2010-04-27 02:24:05 +04:00
rval = openprom_ioctl ( file , cmd , arg ) ;
2005-11-08 01:12:47 +03:00
break ;
}
2005-11-08 21:00:13 +03:00
return rval ;
2005-11-08 01:12:47 +03:00
}
2005-04-17 02:20:36 +04:00
static int openprom_open ( struct inode * inode , struct file * file )
{
DATA * data ;
2006-06-26 10:19:30 +04:00
data = kmalloc ( sizeof ( DATA ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - ENOMEM ;
2010-07-21 10:36:39 +04:00
mutex_lock ( & openprom_mutex ) ;
2006-06-26 10:19:30 +04:00
data - > current_node = of_find_node_by_path ( " / " ) ;
data - > lastnode = data - > current_node ;
file - > private_data = ( void * ) data ;
2010-07-21 10:36:39 +04:00
mutex_unlock ( & openprom_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int openprom_release ( struct inode * inode , struct file * file )
{
kfree ( file - > private_data ) ;
return 0 ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations openprom_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
2010-04-27 02:24:05 +04:00
. unlocked_ioctl = openprom_ioctl ,
2005-11-08 21:00:13 +03:00
. compat_ioctl = openprom_compat_ioctl ,
2005-04-17 02:20:36 +04:00
. open = openprom_open ,
. release = openprom_release ,
} ;
static struct miscdevice openprom_dev = {
2006-06-26 10:19:30 +04:00
. minor = SUN_OPENPROM_MINOR ,
. name = " openprom " ,
. fops = & openprom_fops ,
2005-04-17 02:20:36 +04:00
} ;
static int __init openprom_init ( void )
{
2006-06-26 10:19:30 +04:00
struct device_node * dp ;
int err ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
err = misc_register ( & openprom_dev ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
dp = of_find_node_by_path ( " / " ) ;
dp = dp - > child ;
while ( dp ) {
if ( ! strcmp ( dp - > name , " options " ) )
break ;
dp = dp - > sibling ;
}
options_node = dp ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:30 +04:00
if ( ! options_node ) {
2005-04-17 02:20:36 +04:00
misc_deregister ( & openprom_dev ) ;
return - EIO ;
}
return 0 ;
}
static void __exit openprom_cleanup ( void )
{
misc_deregister ( & openprom_dev ) ;
}
module_init ( openprom_init ) ;
module_exit ( openprom_cleanup ) ;