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 .
* http : //home.t-online.de/home/gunther.mayer/lsescd
*
* 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>
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 ;
static int proc_read_pnpconfig ( char * buf , char * * start , off_t pos ,
2007-07-26 21:41:20 +04:00
int count , int * eof , void * data )
2005-04-17 02:20:36 +04:00
{
struct pnp_isa_config_struc pnps ;
if ( pnp_bios_isapnp_config ( & pnps ) )
return - EIO ;
return snprintf ( buf , count ,
2007-07-26 21:41:20 +04:00
" 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 ) ;
2005-04-17 02:20:36 +04:00
}
static int proc_read_escdinfo ( char * buf , char * * start , off_t pos ,
2007-07-26 21:41:20 +04:00
int count , int * eof , void * data )
2005-04-17 02:20:36 +04:00
{
struct escd_info_struc escd ;
if ( pnp_bios_escd_info ( & escd ) )
return - EIO ;
return snprintf ( buf , count ,
2007-07-26 21:41:20 +04:00
" min_ESCD_write_size %d \n "
" ESCD_size %d \n "
" NVRAM_base 0x%x \n " ,
escd . min_escd_write_size ,
escd . escd_size , escd . nv_storage_base ) ;
2005-04-17 02:20:36 +04:00
}
# define MAX_SANE_ESCD_SIZE (32*1024)
static int proc_read_escd ( char * buf , char * * start , off_t pos ,
2007-07-26 21:41:20 +04:00
int count , int * eof , void * data )
2005-04-17 02:20:36 +04:00
{
struct escd_info_struc escd ;
char * tmpbuf ;
int escd_size , escd_left_to_read , n ;
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
" PnPBIOS: proc_read_escd: ESCD size reported by BIOS escd_info call is too great \n " ) ;
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 ) {
2007-10-17 10:26:56 +04:00
printk ( KERN_ERR " PnPBIOS: proc_read_escd: ESCD size reported by "
" BIOS read_escd call is too great \n " ) ;
kfree ( tmpbuf ) ;
2005-04-17 02:20:36 +04:00
return - EFBIG ;
}
escd_left_to_read = escd_size - pos ;
2007-07-26 21:41:20 +04:00
if ( escd_left_to_read < 0 )
escd_left_to_read = 0 ;
if ( escd_left_to_read = = 0 )
* eof = 1 ;
n = min ( count , escd_left_to_read ) ;
2005-04-17 02:20:36 +04:00
memcpy ( buf , tmpbuf + pos , n ) ;
kfree ( tmpbuf ) ;
* start = buf ;
return n ;
}
static int proc_read_legacyres ( char * buf , char * * start , off_t pos ,
2007-07-26 21:41:20 +04:00
int count , int * eof , void * data )
2005-04-17 02:20:36 +04:00
{
/* Assume that the following won't overflow the buffer */
2007-07-26 21:41:20 +04:00
if ( pnp_bios_get_stat_res ( buf ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
2007-07-26 21:41:20 +04:00
return count ; // FIXME: Return actual length
2005-04-17 02:20:36 +04:00
}
static int proc_read_devices ( char * buf , char * * start , off_t pos ,
2007-07-26 21:41:20 +04:00
int count , int * eof , void * data )
2005-04-17 02:20:36 +04:00
{
struct pnp_bios_node * node ;
u8 nodenum ;
char * p = buf ;
if ( pos > = 0xff )
return 0 ;
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
2007-07-26 21:41:20 +04:00
for ( nodenum = pos ; nodenum < 0xff ; ) {
2005-04-17 02:20:36 +04:00
u8 thisnodenum = nodenum ;
/* 26 = the number of characters per line sprintf'ed */
if ( ( p - buf + 26 ) > count )
break ;
if ( pnp_bios_get_dev_node ( & nodenum , PNPMODE_DYNAMIC , node ) )
break ;
p + = sprintf ( p , " %02x \t %08x \t %02x:%02x:%02x \t %04x \n " ,
node - > handle , node - > eisa_id ,
node - > type_code [ 0 ] , node - > type_code [ 1 ] ,
node - > type_code [ 2 ] , node - > flags ) ;
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
* eof = 1 ;
break ;
}
}
kfree ( node ) ;
if ( nodenum = = 0xff )
* eof = 1 ;
2007-07-26 21:41:20 +04:00
* start = ( char * ) ( ( off_t ) nodenum - pos ) ;
2005-04-17 02:20:36 +04:00
return p - buf ;
}
static int proc_read_node ( char * buf , char * * start , off_t pos ,
2007-07-26 21:41:20 +04:00
int count , int * eof , void * data )
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 ) ;
memcpy ( buf , node - > data , len ) ;
kfree ( node ) ;
return len ;
}
2007-07-26 21:41:20 +04:00
static int proc_write_node ( struct file * file , const char __user * buf ,
unsigned long count , void * data )
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 ;
}
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 ] ;
struct proc_dir_entry * ent ;
sprintf ( name , " %02x " , node - > handle ) ;
if ( ! proc_pnp )
return - EIO ;
2007-07-26 21:41:20 +04:00
if ( ! pnpbios_dont_use_current_config ) {
2005-04-17 02:20:36 +04:00
ent = create_proc_entry ( name , 0 , proc_pnp ) ;
if ( ent ) {
ent - > read_proc = proc_read_node ;
ent - > write_proc = proc_write_node ;
ent - > data = ( void * ) ( long ) ( node - > handle ) ;
}
}
if ( ! proc_pnp_boot )
return - EIO ;
ent = create_proc_entry ( name , 0 , proc_pnp_boot ) ;
if ( ent ) {
ent - > read_proc = proc_read_node ;
ent - > write_proc = proc_write_node ;
2007-07-26 21:41:20 +04:00
ent - > data = ( 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 ;
create_proc_read_entry ( " devices " , 0 , proc_pnp , proc_read_devices , NULL ) ;
2007-07-26 21:41:20 +04:00
create_proc_read_entry ( " configuration_info " , 0 , proc_pnp ,
proc_read_pnpconfig , NULL ) ;
create_proc_read_entry ( " escd_info " , 0 , proc_pnp , proc_read_escdinfo ,
NULL ) ;
2005-04-17 02:20:36 +04:00
create_proc_read_entry ( " escd " , S_IRUSR , proc_pnp , proc_read_escd , NULL ) ;
2007-07-26 21:41:20 +04:00
create_proc_read_entry ( " legacy_device_resources " , 0 , proc_pnp ,
proc_read_legacyres , NULL ) ;
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
}