2005-04-17 02:20:36 +04:00
/*
* event . c - exporting ACPI events via procfs
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
*
*/
# include <linux/spinlock.h>
# include <linux/proc_fs.h>
# include <linux/init.h>
# include <linux/poll.h>
# include <acpi/acpi_drivers.h>
2007-06-19 07:40:03 +04:00
# include <net/netlink.h>
# include <net/genetlink.h>
2005-04-17 02:20:36 +04:00
# define _COMPONENT ACPI_SYSTEM_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " event " ) ;
2005-04-17 02:20:36 +04:00
2007-08-23 23:20:26 +04:00
# ifdef CONFIG_ACPI_PROC_EVENT
2005-04-17 02:20:36 +04:00
/* Global vars for handling event proc entry */
static DEFINE_SPINLOCK ( acpi_system_event_lock ) ;
2005-08-05 08:44:28 +04:00
int event_is_open = 0 ;
extern struct list_head acpi_bus_event_list ;
extern wait_queue_head_t acpi_bus_event_queue ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
static int acpi_system_open_event ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:37:45 +04:00
spin_lock_irq ( & acpi_system_event_lock ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:37:45 +04:00
if ( event_is_open )
2005-04-17 02:20:36 +04:00
goto out_busy ;
event_is_open = 1 ;
2005-08-05 08:37:45 +04:00
spin_unlock_irq ( & acpi_system_event_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-08-05 08:44:28 +04:00
out_busy :
2005-08-05 08:37:45 +04:00
spin_unlock_irq ( & acpi_system_event_lock ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_system_read_event ( struct file * file , char __user * buffer , size_t count ,
loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_bus_event event ;
static char str [ ACPI_MAX_STRING ] ;
static int chars_remaining = 0 ;
static char * ptr ;
2005-04-17 02:20:36 +04:00
if ( ! chars_remaining ) {
memset ( & event , 0 , sizeof ( struct acpi_bus_event ) ) ;
if ( ( file - > f_flags & O_NONBLOCK )
& & ( list_empty ( & acpi_bus_event_list ) ) )
2006-06-27 08:41:40 +04:00
return - EAGAIN ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_receive_event ( & event ) ;
2005-10-18 03:43:31 +04:00
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
chars_remaining = sprintf ( str , " %s %s %08x %08x \n " ,
event . device_class ? event .
device_class : " <unknown> " ,
event . bus_id ? event .
bus_id : " <unknown> " , event . type ,
event . data ) ;
2005-04-17 02:20:36 +04:00
ptr = str ;
}
if ( chars_remaining < count ) {
count = chars_remaining ;
}
if ( copy_to_user ( buffer , ptr , count ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
* ppos + = count ;
chars_remaining - = count ;
ptr + = count ;
2006-06-27 08:41:40 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_system_close_event ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
spin_lock_irq ( & acpi_system_event_lock ) ;
2005-04-17 02:20:36 +04:00
event_is_open = 0 ;
2005-08-05 08:44:28 +04:00
spin_unlock_irq ( & acpi_system_event_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-08-05 08:44:28 +04:00
static unsigned int acpi_system_poll_event ( struct file * file , poll_table * wait )
2005-04-17 02:20:36 +04:00
{
poll_wait ( file , & acpi_bus_event_queue , wait ) ;
if ( ! list_empty ( & acpi_bus_event_list ) )
return POLLIN | POLLRDNORM ;
return 0 ;
}
2006-07-04 21:06:00 +04:00
static const struct file_operations acpi_system_event_ops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_system_open_event ,
. read = acpi_system_read_event ,
. release = acpi_system_close_event ,
. poll = acpi_system_poll_event ,
2005-04-17 02:20:36 +04:00
} ;
2007-08-23 23:20:26 +04:00
# endif /* CONFIG_ACPI_PROC_EVENT */
2005-04-17 02:20:36 +04:00
2008-01-25 09:48:06 +03:00
/* ACPI notifier chain */
2008-02-14 00:29:57 +03:00
static BLOCKING_NOTIFIER_HEAD ( acpi_chain_head ) ;
2008-01-25 09:48:06 +03:00
int acpi_notifier_call_chain ( struct acpi_device * dev , u32 type , u32 data )
{
struct acpi_bus_event event ;
strcpy ( event . device_class , dev - > pnp . device_class ) ;
strcpy ( event . bus_id , dev - > pnp . bus_id ) ;
event . type = type ;
event . data = data ;
return ( blocking_notifier_call_chain ( & acpi_chain_head , 0 , ( void * ) & event )
= = NOTIFY_BAD ) ? - EINVAL : 0 ;
}
EXPORT_SYMBOL ( acpi_notifier_call_chain ) ;
int register_acpi_notifier ( struct notifier_block * nb )
{
return blocking_notifier_chain_register ( & acpi_chain_head , nb ) ;
}
EXPORT_SYMBOL ( register_acpi_notifier ) ;
int unregister_acpi_notifier ( struct notifier_block * nb )
{
return blocking_notifier_chain_unregister ( & acpi_chain_head , nb ) ;
}
EXPORT_SYMBOL ( unregister_acpi_notifier ) ;
2007-06-19 07:40:03 +04:00
# ifdef CONFIG_NET
2007-08-11 00:45:18 +04:00
static unsigned int acpi_event_seqnum ;
2007-06-19 07:40:03 +04:00
struct acpi_genl_event {
acpi_device_class device_class ;
char bus_id [ 15 ] ;
u32 type ;
u32 data ;
} ;
/* attributes of acpi_genl_family */
enum {
ACPI_GENL_ATTR_UNSPEC ,
ACPI_GENL_ATTR_EVENT , /* ACPI event info needed by user space */
__ACPI_GENL_ATTR_MAX ,
} ;
# define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)
/* commands supported by the acpi_genl_family */
enum {
ACPI_GENL_CMD_UNSPEC ,
ACPI_GENL_CMD_EVENT , /* kernel->user notifications for ACPI events */
__ACPI_GENL_CMD_MAX ,
} ;
# define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)
2007-07-20 12:41:14 +04:00
# define ACPI_GENL_FAMILY_NAME "acpi_event"
# define ACPI_GENL_VERSION 0x01
# define ACPI_GENL_MCAST_GROUP_NAME "acpi_mc_group"
2007-06-19 07:40:03 +04:00
static struct genl_family acpi_event_genl_family = {
. id = GENL_ID_GENERATE ,
2007-07-20 12:41:14 +04:00
. name = ACPI_GENL_FAMILY_NAME ,
2007-06-19 07:40:03 +04:00
. version = ACPI_GENL_VERSION ,
. maxattr = ACPI_GENL_ATTR_MAX ,
} ;
2007-07-20 12:41:14 +04:00
static struct genl_multicast_group acpi_event_mcgrp = {
. name = ACPI_GENL_MCAST_GROUP_NAME ,
2007-06-19 07:40:03 +04:00
} ;
2007-08-22 21:24:31 +04:00
int acpi_bus_generate_netlink_event ( const char * device_class ,
const char * bus_id ,
2007-06-19 07:40:03 +04:00
u8 type , int data )
{
struct sk_buff * skb ;
struct nlattr * attr ;
struct acpi_genl_event * event ;
void * msg_header ;
int size ;
int result ;
/* allocate memory */
size = nla_total_size ( sizeof ( struct acpi_genl_event ) ) +
nla_total_size ( 0 ) ;
skb = genlmsg_new ( size , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
/* add the genetlink message header */
msg_header = genlmsg_put ( skb , 0 , acpi_event_seqnum + + ,
& acpi_event_genl_family , 0 ,
ACPI_GENL_CMD_EVENT ) ;
if ( ! msg_header ) {
nlmsg_free ( skb ) ;
return - ENOMEM ;
}
/* fill the data */
attr =
nla_reserve ( skb , ACPI_GENL_ATTR_EVENT ,
sizeof ( struct acpi_genl_event ) ) ;
if ( ! attr ) {
nlmsg_free ( skb ) ;
return - EINVAL ;
}
event = nla_data ( attr ) ;
if ( ! event ) {
nlmsg_free ( skb ) ;
return - EINVAL ;
}
memset ( event , 0 , sizeof ( struct acpi_genl_event ) ) ;
2007-08-22 21:24:31 +04:00
strcpy ( event - > device_class , device_class ) ;
strcpy ( event - > bus_id , bus_id ) ;
2007-06-19 07:40:03 +04:00
event - > type = type ;
event - > data = data ;
/* send multicast genetlink message */
result = genlmsg_end ( skb , msg_header ) ;
if ( result < 0 ) {
nlmsg_free ( skb ) ;
return result ;
}
result =
2007-07-20 12:41:14 +04:00
genlmsg_multicast ( skb , 0 , acpi_event_mcgrp . id , GFP_ATOMIC ) ;
2007-06-19 07:40:03 +04:00
if ( result )
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Failed to send a Genetlink message! \n " ) ) ;
return 0 ;
}
2007-08-22 21:24:31 +04:00
EXPORT_SYMBOL ( acpi_bus_generate_netlink_event ) ;
2007-06-19 07:40:03 +04:00
static int acpi_event_genetlink_init ( void )
{
int result ;
result = genl_register_family ( & acpi_event_genl_family ) ;
if ( result )
return result ;
2007-07-20 12:41:14 +04:00
result = genl_register_mc_group ( & acpi_event_genl_family ,
& acpi_event_mcgrp ) ;
2007-06-19 07:40:03 +04:00
if ( result )
genl_unregister_family ( & acpi_event_genl_family ) ;
return result ;
}
# else
2007-08-24 11:06:33 +04:00
int acpi_bus_generate_netlink_event ( const char * device_class ,
const char * bus_id ,
u8 type , int data )
2007-06-19 07:40:03 +04:00
{
return 0 ;
}
2007-09-03 07:03:35 +04:00
EXPORT_SYMBOL ( acpi_bus_generate_netlink_event ) ;
2007-08-22 21:24:31 +04:00
2007-06-19 07:40:03 +04:00
static int acpi_event_genetlink_init ( void )
{
return - ENODEV ;
}
# endif
2005-04-17 02:20:36 +04:00
static int __init acpi_event_init ( void )
{
2007-08-23 23:20:26 +04:00
# ifdef CONFIG_ACPI_PROC_EVENT
2005-08-05 08:44:28 +04:00
struct proc_dir_entry * entry ;
2007-08-23 23:20:26 +04:00
# endif
2005-04-17 02:20:36 +04:00
int error = 0 ;
if ( acpi_disabled )
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-06-19 07:40:03 +04:00
/* create genetlink for acpi event */
error = acpi_event_genetlink_init ( ) ;
if ( error )
printk ( KERN_WARNING PREFIX
" Failed to create genetlink family for ACPI event \n " ) ;
2007-08-23 23:20:26 +04:00
# ifdef CONFIG_ACPI_PROC_EVENT
2005-04-17 02:20:36 +04:00
/* 'event' [R] */
2008-04-29 12:02:27 +04:00
entry = proc_create ( " event " , S_IRUSR , acpi_root_dir ,
& acpi_system_event_ops ) ;
if ( ! entry )
2007-06-19 07:40:03 +04:00
return - ENODEV ;
2007-08-23 23:20:26 +04:00
# endif
2007-06-19 07:40:03 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-06-19 07:40:03 +04:00
fs_initcall ( acpi_event_init ) ;