2005-04-17 02:20:36 +04:00
/*
* / proc / bus / pnp interface for Plug and Play devices
*
* Written by David Hinds , dahinds @ users . sourceforge . net
* Modified by Thomas Hood
*
* The . . . / devices and . . . / < node > and . . . / boot / < node > files are
* utilized by the lspnp and setpnp utilities , supplied with the
* pcmcia - cs package .
* http : //pcmcia-cs.sourceforge.net
*
* The . . . / escd file is utilized by the lsescd utility written by
* Gunther Mayer .
*
* The . . . / legacy_device_resources file is not used yet .
*
* The other files are human - readable .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/proc_fs.h>
2008-04-29 02:34:42 +04:00
# include <linux/pnp.h>
2009-12-16 03:46:47 +03:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <asm/uaccess.h>
# include "pnpbios.h"
static struct proc_dir_entry * proc_pnp = NULL ;
static struct proc_dir_entry * proc_pnp_boot = NULL ;
2009-12-16 03:46:47 +03:00
static int pnpconfig_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
struct pnp_isa_config_struc pnps ;
if ( pnp_bios_isapnp_config ( & pnps ) )
return - EIO ;
2009-12-16 03:46:47 +03:00
seq_printf ( m , " structure_revision %d \n "
" number_of_CSNs %d \n "
" ISA_read_data_port 0x%x \n " ,
pnps . revision , pnps . no_csns , pnps . isa_rd_data_port ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-12-16 03:46:47 +03:00
static int pnpconfig_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , pnpconfig_proc_show , NULL ) ;
}
static const struct file_operations pnpconfig_proc_fops = {
. owner = THIS_MODULE ,
. open = pnpconfig_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int escd_info_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
struct escd_info_struc escd ;
if ( pnp_bios_escd_info ( & escd ) )
return - EIO ;
2009-12-16 03:46:47 +03:00
seq_printf ( m , " min_ESCD_write_size %d \n "
2007-07-26 21:41:20 +04:00
" ESCD_size %d \n "
" NVRAM_base 0x%x \n " ,
escd . min_escd_write_size ,
escd . escd_size , escd . nv_storage_base ) ;
2009-12-16 03:46:47 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-12-16 03:46:47 +03:00
static int escd_info_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , escd_info_proc_show , NULL ) ;
}
static const struct file_operations escd_info_proc_fops = {
. owner = THIS_MODULE ,
. open = escd_info_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2005-04-17 02:20:36 +04:00
# define MAX_SANE_ESCD_SIZE (32*1024)
2009-12-16 03:46:47 +03:00
static int escd_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
struct escd_info_struc escd ;
char * tmpbuf ;
2009-12-16 03:46:47 +03:00
int escd_size ;
2005-04-17 02:20:36 +04:00
if ( pnp_bios_escd_info ( & escd ) )
return - EIO ;
/* sanity check */
if ( escd . escd_size > MAX_SANE_ESCD_SIZE ) {
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
2009-12-16 03:46:47 +03:00
" PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return - EFBIG ;
}
2006-12-13 11:34:52 +03:00
tmpbuf = kzalloc ( escd . escd_size , GFP_KERNEL ) ;
2007-07-26 21:41:20 +04:00
if ( ! tmpbuf )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
if ( pnp_bios_read_escd ( tmpbuf , escd . nv_storage_base ) ) {
kfree ( tmpbuf ) ;
return - EIO ;
}
2007-07-26 21:41:20 +04:00
escd_size =
( unsigned char ) ( tmpbuf [ 0 ] ) + ( unsigned char ) ( tmpbuf [ 1 ] ) * 256 ;
2005-04-17 02:20:36 +04:00
/* sanity check */
if ( escd_size > MAX_SANE_ESCD_SIZE ) {
2009-12-16 03:46:47 +03:00
printk ( KERN_ERR " PnPBIOS: %s: ESCD size reported by "
" BIOS read_escd call is too great \n " , __func__ ) ;
2007-10-17 10:26:56 +04:00
kfree ( tmpbuf ) ;
2005-04-17 02:20:36 +04:00
return - EFBIG ;
}
2009-12-16 03:46:47 +03:00
seq_write ( m , tmpbuf , escd_size ) ;
2005-04-17 02:20:36 +04:00
kfree ( tmpbuf ) ;
2009-12-16 03:46:47 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-12-16 03:46:47 +03:00
static int escd_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , escd_proc_show , NULL ) ;
}
static const struct file_operations escd_proc_fops = {
. owner = THIS_MODULE ,
. open = escd_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int pnp_legacyres_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
2009-12-16 03:46:47 +03:00
void * buf ;
buf = kmalloc ( 65536 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
if ( pnp_bios_get_stat_res ( buf ) ) {
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
2009-12-16 03:46:47 +03:00
}
seq_write ( m , buf , 65536 ) ;
kfree ( buf ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2009-12-16 03:46:47 +03:00
static int pnp_legacyres_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , pnp_legacyres_proc_show , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2009-12-16 03:46:47 +03:00
static const struct file_operations pnp_legacyres_proc_fops = {
. owner = THIS_MODULE ,
. open = pnp_legacyres_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int pnp_devices_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
struct pnp_bios_node * node ;
u8 nodenum ;
2006-12-13 11:34:52 +03:00
node = kzalloc ( node_info . max_node_size , GFP_KERNEL ) ;
2007-07-26 21:41:20 +04:00
if ( ! node )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2009-12-16 03:46:47 +03:00
for ( nodenum = 0 ; nodenum < 0xff ; ) {
2005-04-17 02:20:36 +04:00
u8 thisnodenum = nodenum ;
2009-12-16 03:46:47 +03:00
2005-04-17 02:20:36 +04:00
if ( pnp_bios_get_dev_node ( & nodenum , PNPMODE_DYNAMIC , node ) )
break ;
2013-04-23 17:37:19 +04:00
seq_printf ( m , " %02x \t %08x \t %3phC \t %04x \n " ,
2005-04-17 02:20:36 +04:00
node - > handle , node - > eisa_id ,
2013-04-23 17:37:19 +04:00
node - > type_code , node - > flags ) ;
2005-04-17 02:20:36 +04:00
if ( nodenum < = thisnodenum ) {
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" %s Node number 0x%x is out of sequence following node 0x%x. Aborting. \n " ,
" PnPBIOS: proc_read_devices: " ,
( unsigned int ) nodenum ,
( unsigned int ) thisnodenum ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
kfree ( node ) ;
2009-12-16 03:46:47 +03:00
return 0 ;
}
static int pnp_devices_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , pnp_devices_proc_show , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2009-12-16 03:46:47 +03:00
static const struct file_operations pnp_devices_proc_fops = {
. owner = THIS_MODULE ,
. open = pnp_devices_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int pnpbios_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
2009-12-16 03:46:47 +03:00
void * data = m - > private ;
2005-04-17 02:20:36 +04:00
struct pnp_bios_node * node ;
int boot = ( long ) data > > 8 ;
u8 nodenum = ( long ) data ;
int len ;
2006-12-13 11:34:52 +03:00
node = kzalloc ( node_info . max_node_size , GFP_KERNEL ) ;
2007-07-26 21:41:20 +04:00
if ( ! node )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
if ( pnp_bios_get_dev_node ( & nodenum , boot , node ) ) {
kfree ( node ) ;
return - EIO ;
}
len = node - > size - sizeof ( struct pnp_bios_node ) ;
2009-12-16 03:46:47 +03:00
seq_write ( m , node - > data , len ) ;
2005-04-17 02:20:36 +04:00
kfree ( node ) ;
2009-12-16 03:46:47 +03:00
return 0 ;
}
static int pnpbios_proc_open ( struct inode * inode , struct file * file )
{
2013-04-01 02:16:14 +04:00
return single_open ( file , pnpbios_proc_show , PDE_DATA ( inode ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-12-16 03:46:47 +03:00
static ssize_t pnpbios_proc_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2013-04-01 02:16:14 +04:00
void * data = PDE_DATA ( file_inode ( file ) ) ;
2005-04-17 02:20:36 +04:00
struct pnp_bios_node * node ;
int boot = ( long ) data > > 8 ;
u8 nodenum = ( long ) data ;
int ret = count ;
2006-12-13 11:34:52 +03:00
node = kzalloc ( node_info . max_node_size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! node )
return - ENOMEM ;
if ( pnp_bios_get_dev_node ( & nodenum , boot , node ) ) {
ret = - EIO ;
goto out ;
}
if ( count ! = node - > size - sizeof ( struct pnp_bios_node ) ) {
ret = - EINVAL ;
goto out ;
}
if ( copy_from_user ( node - > data , buf , count ) ) {
ret = - EFAULT ;
goto out ;
}
if ( pnp_bios_set_dev_node ( node - > handle , boot , node ) ! = 0 ) {
ret = - EINVAL ;
goto out ;
}
ret = count ;
2007-08-15 20:32:08 +04:00
out :
2005-04-17 02:20:36 +04:00
kfree ( node ) ;
return ret ;
}
2009-12-16 03:46:47 +03:00
static const struct file_operations pnpbios_proc_fops = {
. owner = THIS_MODULE ,
. open = pnpbios_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = pnpbios_proc_write ,
} ;
2007-07-26 21:41:20 +04:00
int pnpbios_interface_attach_device ( struct pnp_bios_node * node )
2005-04-17 02:20:36 +04:00
{
char name [ 3 ] ;
sprintf ( name , " %02x " , node - > handle ) ;
if ( ! proc_pnp )
return - EIO ;
2007-07-26 21:41:20 +04:00
if ( ! pnpbios_dont_use_current_config ) {
2009-12-16 03:46:47 +03:00
proc_create_data ( name , 0644 , proc_pnp , & pnpbios_proc_fops ,
( void * ) ( long ) ( node - > handle ) ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! proc_pnp_boot )
return - EIO ;
2009-12-16 03:46:47 +03:00
if ( proc_create_data ( name , 0644 , proc_pnp_boot , & pnpbios_proc_fops ,
( void * ) ( long ) ( node - > handle + 0x100 ) ) )
2005-04-17 02:20:36 +04:00
return 0 ;
return - EIO ;
}
/*
* When this is called , pnpbios functions are assumed to
* work and the pnpbios_dont_use_current_config flag
* should already have been set to the appropriate value
*/
2007-07-26 21:41:20 +04:00
int __init pnpbios_proc_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-04-29 12:01:41 +04:00
proc_pnp = proc_mkdir ( " bus/pnp " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! proc_pnp )
return - EIO ;
proc_pnp_boot = proc_mkdir ( " boot " , proc_pnp ) ;
if ( ! proc_pnp_boot )
return - EIO ;
2009-12-16 03:46:47 +03:00
proc_create ( " devices " , 0 , proc_pnp , & pnp_devices_proc_fops ) ;
proc_create ( " configuration_info " , 0 , proc_pnp , & pnpconfig_proc_fops ) ;
proc_create ( " escd_info " , 0 , proc_pnp , & escd_info_proc_fops ) ;
proc_create ( " escd " , S_IRUSR , proc_pnp , & escd_proc_fops ) ;
proc_create ( " legacy_device_resources " , 0 , proc_pnp , & pnp_legacyres_proc_fops ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
void __exit pnpbios_proc_exit ( void )
{
int i ;
char name [ 3 ] ;
if ( ! proc_pnp )
return ;
2007-07-26 21:41:20 +04:00
for ( i = 0 ; i < 0xff ; i + + ) {
2005-04-17 02:20:36 +04:00
sprintf ( name , " %02x " , i ) ;
2007-07-26 21:41:20 +04:00
if ( ! pnpbios_dont_use_current_config )
2005-04-17 02:20:36 +04:00
remove_proc_entry ( name , proc_pnp ) ;
remove_proc_entry ( name , proc_pnp_boot ) ;
}
remove_proc_entry ( " legacy_device_resources " , proc_pnp ) ;
remove_proc_entry ( " escd " , proc_pnp ) ;
remove_proc_entry ( " escd_info " , proc_pnp ) ;
remove_proc_entry ( " configuration_info " , proc_pnp ) ;
remove_proc_entry ( " devices " , proc_pnp ) ;
remove_proc_entry ( " boot " , proc_pnp ) ;
2008-04-29 12:01:41 +04:00
remove_proc_entry ( " bus/pnp " , NULL ) ;
2005-04-17 02:20:36 +04:00
}