2007-04-30 07:42:45 +04:00
/*
* Generic implementation of a polled input device
* Copyright ( c ) 2007 Dmitry Torokhov
*
* 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 .
*/
# include <linux/jiffies.h>
# include <linux/mutex.h>
# include <linux/input-polldev.h>
2007-05-23 07:28:03 +04:00
MODULE_AUTHOR ( " Dmitry Torokhov <dtor@mail.ru> " ) ;
MODULE_DESCRIPTION ( " Generic implementation of a polled input device " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_VERSION ( " 0.1 " ) ;
2007-04-30 07:42:45 +04:00
static DEFINE_MUTEX ( polldev_mutex ) ;
static int polldev_users ;
static struct workqueue_struct * polldev_wq ;
static int input_polldev_start_workqueue ( void )
{
int retval ;
retval = mutex_lock_interruptible ( & polldev_mutex ) ;
if ( retval )
return retval ;
if ( ! polldev_users ) {
polldev_wq = create_singlethread_workqueue ( " ipolldevd " ) ;
if ( ! polldev_wq ) {
printk ( KERN_ERR " input-polldev: failed to create "
" ipolldevd workqueue \n " ) ;
retval = - ENOMEM ;
goto out ;
}
}
polldev_users + + ;
out :
mutex_unlock ( & polldev_mutex ) ;
return retval ;
}
static void input_polldev_stop_workqueue ( void )
{
mutex_lock ( & polldev_mutex ) ;
if ( ! - - polldev_users )
destroy_workqueue ( polldev_wq ) ;
mutex_unlock ( & polldev_mutex ) ;
}
2009-11-14 08:13:22 +03:00
static void input_polldev_queue_work ( struct input_polled_dev * dev )
2007-04-30 07:42:45 +04:00
{
2007-11-21 22:03:37 +03:00
unsigned long delay ;
2007-04-30 07:42:45 +04:00
2007-11-21 22:03:37 +03:00
delay = msecs_to_jiffies ( dev - > poll_interval ) ;
if ( delay > = HZ )
delay = round_jiffies_relative ( delay ) ;
queue_delayed_work ( polldev_wq , & dev - > work , delay ) ;
2007-04-30 07:42:45 +04:00
}
2009-11-14 08:13:22 +03:00
static void input_polled_device_work ( struct work_struct * work )
{
struct input_polled_dev * dev =
container_of ( work , struct input_polled_dev , work . work ) ;
dev - > poll ( dev ) ;
input_polldev_queue_work ( dev ) ;
}
2007-04-30 07:42:45 +04:00
static int input_open_polled_device ( struct input_dev * input )
{
2008-04-02 08:41:00 +04:00
struct input_polled_dev * dev = input_get_drvdata ( input ) ;
2007-04-30 07:42:45 +04:00
int error ;
error = input_polldev_start_workqueue ( ) ;
if ( error )
return error ;
2009-10-18 11:38:57 +04:00
if ( dev - > open )
dev - > open ( dev ) ;
2007-04-30 07:42:45 +04:00
2009-11-23 21:01:33 +03:00
/* Only start polling if polling is enabled */
if ( dev - > poll_interval > 0 )
queue_delayed_work ( polldev_wq , & dev - > work , 0 ) ;
2007-04-30 07:42:45 +04:00
return 0 ;
}
static void input_close_polled_device ( struct input_dev * input )
{
2008-04-02 08:41:00 +04:00
struct input_polled_dev * dev = input_get_drvdata ( input ) ;
2007-04-30 07:42:45 +04:00
2007-11-21 22:03:37 +03:00
cancel_delayed_work_sync ( & dev - > work ) ;
2007-04-30 07:42:45 +04:00
input_polldev_stop_workqueue ( ) ;
2009-10-18 11:38:57 +04:00
if ( dev - > close )
dev - > close ( dev ) ;
2007-04-30 07:42:45 +04:00
}
2009-11-14 08:13:22 +03:00
/* SYSFS interface */
static ssize_t input_polldev_get_poll ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct input_polled_dev * polldev = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %d \n " , polldev - > poll_interval ) ;
}
static ssize_t input_polldev_set_poll ( struct device * dev ,
struct device_attribute * attr , const char * buf ,
size_t count )
{
struct input_polled_dev * polldev = dev_get_drvdata ( dev ) ;
struct input_dev * input = polldev - > input ;
unsigned long interval ;
if ( strict_strtoul ( buf , 0 , & interval ) )
return - EINVAL ;
if ( interval < polldev - > poll_interval_min )
return - EINVAL ;
if ( interval > polldev - > poll_interval_max )
return - EINVAL ;
mutex_lock ( & input - > mutex ) ;
polldev - > poll_interval = interval ;
if ( input - > users ) {
cancel_delayed_work_sync ( & polldev - > work ) ;
if ( polldev - > poll_interval > 0 )
input_polldev_queue_work ( polldev ) ;
}
mutex_unlock ( & input - > mutex ) ;
return count ;
}
static DEVICE_ATTR ( poll , S_IRUGO | S_IWUSR , input_polldev_get_poll ,
input_polldev_set_poll ) ;
static ssize_t input_polldev_get_max ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct input_polled_dev * polldev = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %d \n " , polldev - > poll_interval_max ) ;
}
static DEVICE_ATTR ( max , S_IRUGO , input_polldev_get_max , NULL ) ;
static ssize_t input_polldev_get_min ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct input_polled_dev * polldev = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %d \n " , polldev - > poll_interval_min ) ;
}
static DEVICE_ATTR ( min , S_IRUGO , input_polldev_get_min , NULL ) ;
static struct attribute * sysfs_attrs [ ] = {
& dev_attr_poll . attr ,
& dev_attr_max . attr ,
& dev_attr_min . attr ,
NULL
} ;
static struct attribute_group input_polldev_attribute_group = {
. attrs = sysfs_attrs
} ;
2007-04-30 07:42:45 +04:00
/**
* input_allocate_polled_device - allocated memory polled device
*
* The function allocates memory for a polled device and also
* for an input device associated with this polled device .
*/
struct input_polled_dev * input_allocate_polled_device ( void )
{
struct input_polled_dev * dev ;
dev = kzalloc ( sizeof ( struct input_polled_dev ) , GFP_KERNEL ) ;
if ( ! dev )
return NULL ;
dev - > input = input_allocate_device ( ) ;
if ( ! dev - > input ) {
kfree ( dev ) ;
return NULL ;
}
return dev ;
}
EXPORT_SYMBOL ( input_allocate_polled_device ) ;
/**
* input_free_polled_device - free memory allocated for polled device
* @ dev : device to free
*
* The function frees memory allocated for polling device and drops
* reference to the associated input device ( if present ) .
*/
void input_free_polled_device ( struct input_polled_dev * dev )
{
if ( dev ) {
input_free_device ( dev - > input ) ;
kfree ( dev ) ;
}
}
EXPORT_SYMBOL ( input_free_polled_device ) ;
/**
* input_register_polled_device - register polled device
* @ dev : device to register
*
* The function registers previously initialized polled input device
* with input layer . The device should be allocated with call to
* input_allocate_polled_device ( ) . Callers should also set up poll ( )
* method and set up capabilities ( id , name , phys , bits ) of the
* corresponing input_dev structure .
*/
int input_register_polled_device ( struct input_polled_dev * dev )
{
struct input_dev * input = dev - > input ;
2009-11-14 08:13:22 +03:00
int error ;
2007-04-30 07:42:45 +04:00
2008-04-02 08:41:00 +04:00
input_set_drvdata ( input , dev ) ;
2007-04-30 07:42:45 +04:00
INIT_DELAYED_WORK ( & dev - > work , input_polled_device_work ) ;
if ( ! dev - > poll_interval )
dev - > poll_interval = 500 ;
2009-11-14 08:13:22 +03:00
if ( ! dev - > poll_interval_max )
dev - > poll_interval_max = dev - > poll_interval ;
2007-04-30 07:42:45 +04:00
input - > open = input_open_polled_device ;
input - > close = input_close_polled_device ;
2009-11-14 08:13:22 +03:00
error = input_register_device ( input ) ;
if ( error )
return error ;
error = sysfs_create_group ( & input - > dev . kobj ,
& input_polldev_attribute_group ) ;
if ( error ) {
input_unregister_device ( input ) ;
return error ;
}
return 0 ;
2007-04-30 07:42:45 +04:00
}
EXPORT_SYMBOL ( input_register_polled_device ) ;
/**
* input_unregister_polled_device - unregister polled device
* @ dev : device to unregister
*
* The function unregisters previously registered polled input
* device from input layer . Polling is stopped and device is
* ready to be freed with call to input_free_polled_device ( ) .
* Callers should not attempt to access dev - > input pointer
* after calling this function .
*/
void input_unregister_polled_device ( struct input_polled_dev * dev )
{
2009-11-14 08:13:22 +03:00
sysfs_remove_group ( & dev - > input - > dev . kobj ,
& input_polldev_attribute_group ) ;
2007-04-30 07:42:45 +04:00
input_unregister_device ( dev - > input ) ;
dev - > input = NULL ;
}
EXPORT_SYMBOL ( input_unregister_polled_device ) ;