2005-04-17 02:20:36 +04:00
/*
* socket_sysfs . c - - most of socket - related sysfs output
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* ( C ) 2003 - 2004 Dominik Brodowski
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/major.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/timer.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/pm.h>
# include <linux/device.h>
2006-01-10 23:20:36 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/irq.h>
# define IN_CARD_SERVICES
# include <pcmcia/cs_types.h>
# include <pcmcia/ss.h>
# include <pcmcia/cs.h>
# include <pcmcia/bulkmem.h>
# include <pcmcia/cistpl.h>
# include <pcmcia/cisreg.h>
# include <pcmcia/ds.h>
# include "cs_internal.h"
# define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_type ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
2005-12-09 01:50:36 +03:00
if ( s - > state & SOCKET_CARDBUS )
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " 32-bit \n " ) ;
2005-12-09 01:50:36 +03:00
return sprintf ( buf , " 16-bit \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_type , 0444 , pccard_show_type , NULL ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_voltage ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
2005-12-09 01:50:36 +03:00
if ( s - > socket . Vcc )
return sprintf ( buf , " %d.%dV \n " , s - > socket . Vcc / 10 ,
s - > socket . Vcc % 10 ) ;
return sprintf ( buf , " X.XV \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_voltage , 0444 , pccard_show_voltage , NULL ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_vpp ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
return sprintf ( buf , " %d.%dV \n " , s - > socket . Vpp / 10 , s - > socket . Vpp % 10 ) ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_vpp , 0444 , pccard_show_vpp , NULL ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_vcc ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
return sprintf ( buf , " %d.%dV \n " , s - > socket . Vcc / 10 , s - > socket . Vcc % 10 ) ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_vcc , 0444 , pccard_show_vcc , NULL ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_store_insert ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
ssize_t ret ;
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! count )
return - EINVAL ;
ret = pcmcia_insert_card ( s ) ;
return ret ? ret : count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_insert , 0200 , NULL , pccard_store_insert ) ;
2005-04-17 02:20:36 +04:00
2006-01-10 21:19:37 +03:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_card_pm_state ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
2006-01-10 21:19:37 +03:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
return sprintf ( buf , " %s \n " , s - > state & SOCKET_SUSPEND ? " off " : " on " ) ;
}
2006-09-12 19:00:10 +04:00
static ssize_t pccard_store_card_pm_state ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2006-01-10 21:19:37 +03:00
{
ssize_t ret = - EINVAL ;
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! count )
return - EINVAL ;
if ( ! ( s - > state & SOCKET_SUSPEND ) & & ! strncmp ( buf , " off " , 3 ) )
ret = pcmcia_suspend_card ( s ) ;
else if ( ( s - > state & SOCKET_SUSPEND ) & & ! strncmp ( buf , " on " , 2 ) )
ret = pcmcia_resume_card ( s ) ;
return ret ? - ENODEV : count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_pm_state , 0644 , pccard_show_card_pm_state , pccard_store_card_pm_state ) ;
2006-01-10 21:19:37 +03:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_store_eject ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
ssize_t ret ;
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! count )
return - EINVAL ;
ret = pcmcia_eject_card ( s ) ;
return ret ? ret : count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_eject , 0200 , NULL , pccard_store_eject ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_irq_mask ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
return sprintf ( buf , " 0x%04x \n " , s - > irq_mask ) ;
}
2006-09-12 19:00:10 +04:00
static ssize_t pccard_store_irq_mask ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
ssize_t ret ;
struct pcmcia_socket * s = to_socket ( dev ) ;
u32 mask ;
if ( ! count )
return - EINVAL ;
ret = sscanf ( buf , " 0x%x \n " , & mask ) ;
if ( ret = = 1 ) {
s - > irq_mask & = mask ;
ret = 0 ;
}
return ret ? ret : count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( card_irq_mask , 0600 , pccard_show_irq_mask , pccard_store_irq_mask ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t pccard_show_resource ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * s = to_socket ( dev ) ;
return sprintf ( buf , " %s \n " , s - > resource_setup_done ? " yes " : " no " ) ;
}
2006-09-12 19:00:10 +04:00
static ssize_t pccard_store_resource ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
struct pcmcia_socket * s = to_socket ( dev ) ;
if ( ! count )
return - EINVAL ;
spin_lock_irqsave ( & s - > lock , flags ) ;
2005-06-28 03:28:17 +04:00
if ( ! s - > resource_setup_done )
2005-04-17 02:20:36 +04:00
s - > resource_setup_done = 1 ;
2005-06-28 03:28:17 +04:00
spin_unlock_irqrestore ( & s - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_lock ( & s - > skt_mutex ) ;
2005-06-28 03:28:17 +04:00
if ( ( s - > callback ) & &
( s - > state & SOCKET_PRESENT ) & &
! ( s - > state & SOCKET_CARDBUS ) ) {
if ( try_module_get ( s - > callback - > owner ) ) {
2006-11-03 18:54:00 +03:00
s - > callback - > requery ( s , 0 ) ;
2005-06-28 03:28:17 +04:00
module_put ( s - > callback - > owner ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & s - > skt_mutex ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( available_resources_setup_done , 0600 , pccard_show_resource , pccard_store_resource ) ;
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:08 +04:00
static ssize_t pccard_extract_cis ( struct pcmcia_socket * s , char * buf , loff_t off , size_t count )
{
tuple_t tuple ;
int status , i ;
loff_t pointer = 0 ;
ssize_t ret = 0 ;
u_char * tuplebuffer ;
u_char * tempbuffer ;
tuplebuffer = kmalloc ( sizeof ( u_char ) * 256 , GFP_KERNEL ) ;
if ( ! tuplebuffer )
return - ENOMEM ;
tempbuffer = kmalloc ( sizeof ( u_char ) * 258 , GFP_KERNEL ) ;
if ( ! tempbuffer ) {
ret = - ENOMEM ;
goto free_tuple ;
}
memset ( & tuple , 0 , sizeof ( tuple_t ) ) ;
tuple . Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON ;
tuple . DesiredTuple = RETURN_FIRST_TUPLE ;
tuple . TupleOffset = 0 ;
status = pccard_get_first_tuple ( s , BIND_FN_ALL , & tuple ) ;
while ( ! status ) {
tuple . TupleData = tuplebuffer ;
tuple . TupleDataMax = 255 ;
memset ( tuplebuffer , 0 , sizeof ( u_char ) * 255 ) ;
status = pccard_get_tuple_data ( s , & tuple ) ;
if ( status )
break ;
if ( off < ( pointer + 2 + tuple . TupleDataLen ) ) {
tempbuffer [ 0 ] = tuple . TupleCode & 0xff ;
tempbuffer [ 1 ] = tuple . TupleLink & 0xff ;
for ( i = 0 ; i < tuple . TupleDataLen ; i + + )
tempbuffer [ i + 2 ] = tuplebuffer [ i ] & 0xff ;
for ( i = 0 ; i < ( 2 + tuple . TupleDataLen ) ; i + + ) {
if ( ( ( i + pointer ) > = off ) & &
( i + pointer ) < ( off + count ) ) {
buf [ ret ] = tempbuffer [ i ] ;
ret + + ;
}
}
}
pointer + = 2 + tuple . TupleDataLen ;
if ( pointer > = ( off + count ) )
break ;
if ( tuple . TupleCode = = CISTPL_END )
break ;
status = pccard_get_next_tuple ( s , BIND_FN_ALL , & tuple ) ;
}
kfree ( tempbuffer ) ;
free_tuple :
kfree ( tuplebuffer ) ;
return ( ret ) ;
}
static ssize_t pccard_show_cis ( struct kobject * kobj , char * buf , loff_t off , size_t count )
{
unsigned int size = 0x200 ;
if ( off > = size )
count = 0 ;
else {
struct pcmcia_socket * s ;
cisinfo_t cisinfo ;
if ( off + count > size )
count = size - off ;
2006-09-12 19:00:10 +04:00
s = to_socket ( container_of ( kobj , struct device , kobj ) ) ;
2005-06-28 03:28:08 +04:00
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
if ( pccard_validate_cis ( s , BIND_FN_ALL , & cisinfo ) )
return - EIO ;
if ( ! cisinfo . Chains )
return - ENODATA ;
count = pccard_extract_cis ( s , buf , off , count ) ;
}
return ( count ) ;
}
2005-06-28 03:28:09 +04:00
static ssize_t pccard_store_cis ( struct kobject * kobj , char * buf , loff_t off , size_t count )
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = to_socket ( container_of ( kobj , struct device , kobj ) ) ;
2005-06-28 03:28:09 +04:00
cisdump_t * cis ;
2006-10-02 00:17:44 +04:00
int error ;
2005-06-28 03:28:09 +04:00
if ( off )
return - EINVAL ;
if ( count > = 0x200 )
return - EINVAL ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
2005-12-11 23:18:26 +03:00
cis = kzalloc ( sizeof ( cisdump_t ) , GFP_KERNEL ) ;
2005-06-28 03:28:09 +04:00
if ( ! cis )
return - ENOMEM ;
cis - > Length = count + 1 ;
memcpy ( cis - > Data , buf , count ) ;
2006-10-02 00:17:44 +04:00
error = pcmcia_replace_cis ( s , cis ) ;
2005-06-28 03:28:09 +04:00
kfree ( cis ) ;
2006-10-02 00:17:44 +04:00
if ( error )
return - EIO ;
2005-06-28 03:28:09 +04:00
2006-10-02 00:17:44 +04:00
mutex_lock ( & s - > skt_mutex ) ;
if ( ( s - > callback ) & & ( s - > state & SOCKET_PRESENT ) & &
! ( s - > state & SOCKET_CARDBUS ) ) {
if ( try_module_get ( s - > callback - > owner ) ) {
2006-11-03 18:54:00 +03:00
s - > callback - > requery ( s , 1 ) ;
2006-10-02 00:17:44 +04:00
module_put ( s - > callback - > owner ) ;
2005-06-28 03:28:09 +04:00
}
}
2006-10-02 00:17:44 +04:00
mutex_unlock ( & s - > skt_mutex ) ;
2005-06-28 03:28:09 +04:00
2006-10-02 00:17:44 +04:00
return count ;
2005-06-28 03:28:09 +04:00
}
2005-06-28 03:28:08 +04:00
2006-09-12 19:00:10 +04:00
static struct device_attribute * pccard_socket_attributes [ ] = {
& dev_attr_card_type ,
& dev_attr_card_voltage ,
& dev_attr_card_vpp ,
& dev_attr_card_vcc ,
& dev_attr_card_insert ,
& dev_attr_card_pm_state ,
& dev_attr_card_eject ,
& dev_attr_card_irq_mask ,
& dev_attr_available_resources_setup_done ,
2005-04-17 02:20:36 +04:00
NULL ,
} ;
2005-06-28 03:28:08 +04:00
static struct bin_attribute pccard_cis_attr = {
2005-06-28 03:28:09 +04:00
. attr = { . name = " cis " , . mode = S_IRUGO | S_IWUSR , . owner = THIS_MODULE } ,
2005-06-28 03:28:08 +04:00
. size = 0x200 ,
. read = pccard_show_cis ,
2005-06-28 03:28:09 +04:00
. write = pccard_store_cis ,
2005-06-28 03:28:08 +04:00
} ;
2006-09-12 19:00:10 +04:00
static int __devinit pccard_sysfs_add_socket ( struct device * dev ,
2005-09-15 11:01:36 +04:00
struct class_interface * class_intf )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct device_attribute * * attr ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
for ( attr = pccard_socket_attributes ; * attr ; attr + + ) {
2006-09-12 19:00:10 +04:00
ret = device_create_file ( dev , * attr ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
break ;
}
2005-06-28 03:28:08 +04:00
if ( ! ret )
2006-09-12 19:00:10 +04:00
ret = sysfs_create_bin_file ( & dev - > kobj , & pccard_cis_attr ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-09-12 19:00:10 +04:00
static void __devexit pccard_sysfs_remove_socket ( struct device * dev ,
2005-09-15 11:01:36 +04:00
struct class_interface * class_intf )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct device_attribute * * attr ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
sysfs_remove_bin_file ( & dev - > kobj , & pccard_cis_attr ) ;
2005-04-17 02:20:36 +04:00
for ( attr = pccard_socket_attributes ; * attr ; attr + + )
2006-09-12 19:00:10 +04:00
device_remove_file ( dev , * attr ) ;
2005-04-17 02:20:36 +04:00
}
struct class_interface pccard_sysfs_interface = {
. class = & pcmcia_socket_class ,
2006-09-12 19:00:10 +04:00
. add_dev = & pccard_sysfs_add_socket ,
. remove_dev = __devexit_p ( & pccard_sysfs_remove_socket ) ,
2005-04-17 02:20:36 +04:00
} ;