2005-04-17 02:20:36 +04:00
/*
* The Serio abstraction module
*
* Copyright ( c ) 1999 - 2004 Vojtech Pavlik
* Copyright ( c ) 2004 Dmitry Torokhov
* Copyright ( c ) 2003 Daniele Bellucci
*/
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Should you need to contact me , the author , you can do so either by
* e - mail - mail your message to < vojtech @ ucw . cz > , or by paper mail :
* Vojtech Pavlik , Simunkova 1594 , Prague 8 , 182 00 Czech Republic
*/
# include <linux/stddef.h>
# include <linux/module.h>
# include <linux/serio.h>
# include <linux/errno.h>
# include <linux/wait.h>
# include <linux/sched.h>
# include <linux/slab.h>
2005-06-28 04:49:45 +04:00
# include <linux/kthread.h>
2006-02-19 08:21:55 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " Serio abstraction core " ) ;
MODULE_LICENSE ( " GPL " ) ;
EXPORT_SYMBOL ( serio_interrupt ) ;
EXPORT_SYMBOL ( __serio_register_port ) ;
EXPORT_SYMBOL ( serio_unregister_port ) ;
2005-06-28 04:49:45 +04:00
EXPORT_SYMBOL ( serio_unregister_child_port ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( __serio_unregister_port_delayed ) ;
EXPORT_SYMBOL ( __serio_register_driver ) ;
EXPORT_SYMBOL ( serio_unregister_driver ) ;
EXPORT_SYMBOL ( serio_open ) ;
EXPORT_SYMBOL ( serio_close ) ;
EXPORT_SYMBOL ( serio_rescan ) ;
EXPORT_SYMBOL ( serio_reconnect ) ;
/*
2006-02-19 08:21:55 +03:00
* serio_mutex protects entire serio subsystem and is taken every time
2005-04-17 02:20:36 +04:00
* serio port or driver registrered or unregistered .
*/
2006-02-19 08:21:55 +03:00
static DEFINE_MUTEX ( serio_mutex ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( serio_list ) ;
2006-01-05 17:38:53 +03:00
static struct bus_type serio_bus ;
2005-04-17 02:20:36 +04:00
2006-07-19 09:14:55 +04:00
static void serio_add_driver ( struct serio_driver * drv ) ;
2005-04-17 02:20:36 +04:00
static void serio_add_port ( struct serio * serio ) ;
static void serio_destroy_port ( struct serio * serio ) ;
static void serio_reconnect_port ( struct serio * serio ) ;
static void serio_disconnect_port ( struct serio * serio ) ;
2005-06-28 04:49:45 +04:00
static int serio_connect_driver ( struct serio * serio , struct serio_driver * drv )
{
int retval ;
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio - > drv_mutex ) ;
2005-06-28 04:49:45 +04:00
retval = drv - > connect ( serio , drv ) ;
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio - > drv_mutex ) ;
2005-06-28 04:49:45 +04:00
return retval ;
}
static int serio_reconnect_driver ( struct serio * serio )
{
int retval = - 1 ;
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio - > drv_mutex ) ;
2005-06-28 04:49:45 +04:00
if ( serio - > drv & & serio - > drv - > reconnect )
retval = serio - > drv - > reconnect ( serio ) ;
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio - > drv_mutex ) ;
2005-06-28 04:49:45 +04:00
return retval ;
}
static void serio_disconnect_driver ( struct serio * serio )
{
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio - > drv_mutex ) ;
2005-06-28 04:49:45 +04:00
if ( serio - > drv )
serio - > drv - > disconnect ( serio ) ;
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio - > drv_mutex ) ;
2005-06-28 04:49:45 +04:00
}
2005-04-17 02:20:36 +04:00
static int serio_match_port ( const struct serio_device_id * ids , struct serio * serio )
{
while ( ids - > type | | ids - > proto ) {
if ( ( ids - > type = = SERIO_ANY | | ids - > type = = serio - > id . type ) & &
( ids - > proto = = SERIO_ANY | | ids - > proto = = serio - > id . proto ) & &
( ids - > extra = = SERIO_ANY | | ids - > extra = = serio - > id . extra ) & &
( ids - > id = = SERIO_ANY | | ids - > id = = serio - > id . id ) )
return 1 ;
ids + + ;
}
return 0 ;
}
/*
* Basic serio - > driver core mappings
*/
static void serio_bind_driver ( struct serio * serio , struct serio_driver * drv )
{
down_write ( & serio_bus . subsys . rwsem ) ;
if ( serio_match_port ( drv - > id_table , serio ) ) {
serio - > dev . driver = & drv - > driver ;
2005-06-28 04:49:45 +04:00
if ( serio_connect_driver ( serio , drv ) ) {
2005-04-17 02:20:36 +04:00
serio - > dev . driver = NULL ;
goto out ;
}
device_bind_driver ( & serio - > dev ) ;
}
out :
up_write ( & serio_bus . subsys . rwsem ) ;
}
static void serio_release_driver ( struct serio * serio )
{
down_write ( & serio_bus . subsys . rwsem ) ;
device_release_driver ( & serio - > dev ) ;
up_write ( & serio_bus . subsys . rwsem ) ;
}
static void serio_find_driver ( struct serio * serio )
{
2006-07-19 09:14:55 +04:00
int error ;
2005-04-17 02:20:36 +04:00
down_write ( & serio_bus . subsys . rwsem ) ;
2006-07-19 09:14:55 +04:00
error = device_attach ( & serio - > dev ) ;
if ( error < 0 )
printk ( KERN_WARNING
" serio: device_attach() failed for %s (%s), error: %d \n " ,
serio - > phys , serio - > name , error ) ;
2005-04-17 02:20:36 +04:00
up_write ( & serio_bus . subsys . rwsem ) ;
}
/*
* Serio event processing .
*/
enum serio_event_type {
SERIO_RESCAN ,
SERIO_RECONNECT ,
SERIO_REGISTER_PORT ,
SERIO_UNREGISTER_PORT ,
SERIO_REGISTER_DRIVER ,
} ;
struct serio_event {
enum serio_event_type type ;
void * object ;
struct module * owner ;
struct list_head node ;
} ;
static DEFINE_SPINLOCK ( serio_event_lock ) ; /* protects serio_event_list */
static LIST_HEAD ( serio_event_list ) ;
static DECLARE_WAIT_QUEUE_HEAD ( serio_wait ) ;
2005-06-28 04:49:45 +04:00
static struct task_struct * serio_task ;
2005-04-17 02:20:36 +04:00
static void serio_queue_event ( void * object , struct module * owner ,
enum serio_event_type event_type )
{
unsigned long flags ;
struct serio_event * event ;
spin_lock_irqsave ( & serio_event_lock , flags ) ;
/*
2005-06-28 04:49:45 +04:00
* Scan event list for the other events for the same serio port ,
2005-04-17 02:20:36 +04:00
* starting with the most recent one . If event is the same we
* do not need add new one . If event is of different type we
* need to add this event and should not look further because
* we need to preseve sequence of distinct events .
2005-06-28 04:49:45 +04:00
*/
2005-04-17 02:20:36 +04:00
list_for_each_entry_reverse ( event , & serio_event_list , node ) {
if ( event - > object = = object ) {
if ( event - > type = = event_type )
goto out ;
break ;
}
}
if ( ( event = kmalloc ( sizeof ( struct serio_event ) , GFP_ATOMIC ) ) ) {
if ( ! try_module_get ( owner ) ) {
printk ( KERN_WARNING " serio: Can't get module reference, dropping event %d \n " , event_type ) ;
2006-03-14 08:13:29 +03:00
kfree ( event ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
event - > type = event_type ;
event - > object = object ;
event - > owner = owner ;
list_add_tail ( & event - > node , & serio_event_list ) ;
wake_up ( & serio_wait ) ;
} else {
printk ( KERN_ERR " serio: Not enough memory to queue event %d \n " , event_type ) ;
}
out :
spin_unlock_irqrestore ( & serio_event_lock , flags ) ;
}
static void serio_free_event ( struct serio_event * event )
{
module_put ( event - > owner ) ;
kfree ( event ) ;
}
static void serio_remove_duplicate_events ( struct serio_event * event )
{
struct list_head * node , * next ;
struct serio_event * e ;
unsigned long flags ;
spin_lock_irqsave ( & serio_event_lock , flags ) ;
list_for_each_safe ( node , next , & serio_event_list ) {
e = list_entry ( node , struct serio_event , node ) ;
if ( event - > object = = e - > object ) {
/*
* If this event is of different type we should not
* look further - we only suppress duplicate events
* that were sent back - to - back .
*/
if ( event - > type ! = e - > type )
break ;
list_del_init ( node ) ;
serio_free_event ( e ) ;
}
}
spin_unlock_irqrestore ( & serio_event_lock , flags ) ;
}
static struct serio_event * serio_get_event ( void )
{
struct serio_event * event ;
struct list_head * node ;
unsigned long flags ;
spin_lock_irqsave ( & serio_event_lock , flags ) ;
if ( list_empty ( & serio_event_list ) ) {
spin_unlock_irqrestore ( & serio_event_lock , flags ) ;
return NULL ;
}
node = serio_event_list . next ;
event = list_entry ( node , struct serio_event , node ) ;
list_del_init ( node ) ;
spin_unlock_irqrestore ( & serio_event_lock , flags ) ;
return event ;
}
2005-11-20 08:56:43 +03:00
static void serio_handle_event ( void )
2005-04-17 02:20:36 +04:00
{
struct serio_event * event ;
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:56:43 +03:00
/*
* Note that we handle only one event here to give swsusp
* a chance to freeze kseriod thread . Serio events should
* be pretty rare so we are not concerned about taking
* performance hit .
*/
if ( ( event = serio_get_event ( ) ) ) {
2005-04-17 02:20:36 +04:00
switch ( event - > type ) {
case SERIO_REGISTER_PORT :
serio_add_port ( event - > object ) ;
break ;
case SERIO_UNREGISTER_PORT :
serio_disconnect_port ( event - > object ) ;
serio_destroy_port ( event - > object ) ;
break ;
case SERIO_RECONNECT :
serio_reconnect_port ( event - > object ) ;
break ;
case SERIO_RESCAN :
serio_disconnect_port ( event - > object ) ;
serio_find_driver ( event - > object ) ;
break ;
case SERIO_REGISTER_DRIVER :
2006-07-19 09:14:55 +04:00
serio_add_driver ( event - > object ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
serio_remove_duplicate_events ( event ) ;
serio_free_event ( event ) ;
}
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Remove all events that have been submitted for a given serio port .
*/
static void serio_remove_pending_events ( struct serio * serio )
{
struct list_head * node , * next ;
struct serio_event * event ;
unsigned long flags ;
spin_lock_irqsave ( & serio_event_lock , flags ) ;
list_for_each_safe ( node , next , & serio_event_list ) {
event = list_entry ( node , struct serio_event , node ) ;
if ( event - > object = = serio ) {
list_del_init ( node ) ;
serio_free_event ( event ) ;
}
}
spin_unlock_irqrestore ( & serio_event_lock , flags ) ;
}
/*
* Destroy child serio port ( if any ) that has not been fully registered yet .
*
* Note that we rely on the fact that port can have only one child and therefore
* only one child registration request can be pending . Additionally , children
* are registered by driver ' s connect ( ) handler so there can ' t be a grandchild
* pending registration together with a child .
*/
static struct serio * serio_get_pending_child ( struct serio * parent )
{
struct serio_event * event ;
struct serio * serio , * child = NULL ;
unsigned long flags ;
spin_lock_irqsave ( & serio_event_lock , flags ) ;
list_for_each_entry ( event , & serio_event_list , node ) {
if ( event - > type = = SERIO_REGISTER_PORT ) {
serio = event - > object ;
if ( serio - > parent = = parent ) {
child = serio ;
break ;
}
}
}
spin_unlock_irqrestore ( & serio_event_lock , flags ) ;
return child ;
}
static int serio_thread ( void * nothing )
{
do {
2005-11-20 08:56:43 +03:00
serio_handle_event ( ) ;
2005-06-28 04:49:45 +04:00
wait_event_interruptible ( serio_wait ,
kthread_should_stop ( ) | | ! list_empty ( & serio_event_list ) ) ;
2005-06-25 10:13:50 +04:00
try_to_freeze ( ) ;
2005-06-28 04:49:45 +04:00
} while ( ! kthread_should_stop ( ) ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG " serio: kseriod exiting \n " ) ;
2005-06-28 04:49:45 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Serio port operations
*/
2005-05-17 14:42:58 +04:00
static ssize_t serio_show_description ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " %s \n " , serio - > name ) ;
}
2005-06-30 09:48:34 +04:00
static ssize_t serio_show_modalias ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " serio:ty%02Xpr%02Xid%02Xex%02X \n " ,
serio - > id . type , serio - > id . proto , serio - > id . id , serio - > id . extra ) ;
}
2005-05-17 14:42:58 +04:00
static ssize_t serio_show_id_type ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " %02x \n " , serio - > id . type ) ;
}
2005-05-17 14:42:58 +04:00
static ssize_t serio_show_id_proto ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " %02x \n " , serio - > id . proto ) ;
}
2005-05-17 14:42:58 +04:00
static ssize_t serio_show_id_id ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " %02x \n " , serio - > id . id ) ;
}
2005-05-17 14:42:58 +04:00
static ssize_t serio_show_id_extra ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " %02x \n " , serio - > id . extra ) ;
}
2005-05-17 08:53:09 +04:00
static DEVICE_ATTR ( type , S_IRUGO , serio_show_id_type , NULL ) ;
static DEVICE_ATTR ( proto , S_IRUGO , serio_show_id_proto , NULL ) ;
static DEVICE_ATTR ( id , S_IRUGO , serio_show_id_id , NULL ) ;
static DEVICE_ATTR ( extra , S_IRUGO , serio_show_id_extra , NULL ) ;
static struct attribute * serio_device_id_attrs [ ] = {
& dev_attr_type . attr ,
& dev_attr_proto . attr ,
& dev_attr_id . attr ,
& dev_attr_extra . attr ,
NULL
} ;
static struct attribute_group serio_id_attr_group = {
. name = " id " ,
. attrs = serio_device_id_attrs ,
} ;
2005-05-17 14:42:58 +04:00
static ssize_t serio_rebind_driver ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
struct device_driver * drv ;
int retval ;
2006-02-19 08:21:55 +03:00
retval = mutex_lock_interruptible ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
return retval ;
retval = count ;
if ( ! strncmp ( buf , " none " , count ) ) {
serio_disconnect_port ( serio ) ;
} else if ( ! strncmp ( buf , " reconnect " , count ) ) {
serio_reconnect_port ( serio ) ;
} else if ( ! strncmp ( buf , " rescan " , count ) ) {
serio_disconnect_port ( serio ) ;
serio_find_driver ( serio ) ;
} else if ( ( drv = driver_find ( buf , & serio_bus ) ) ! = NULL ) {
serio_disconnect_port ( serio ) ;
serio_bind_driver ( serio , to_serio_driver ( drv ) ) ;
put_driver ( drv ) ;
} else {
retval = - EINVAL ;
}
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2005-05-17 14:42:58 +04:00
static ssize_t serio_show_bind_mode ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
return sprintf ( buf , " %s \n " , serio - > manual_bind ? " manual " : " auto " ) ;
}
2005-05-17 14:42:58 +04:00
static ssize_t serio_set_bind_mode ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct serio * serio = to_serio_port ( dev ) ;
int retval ;
retval = count ;
if ( ! strncmp ( buf , " manual " , count ) ) {
serio - > manual_bind = 1 ;
} else if ( ! strncmp ( buf , " auto " , count ) ) {
serio - > manual_bind = 0 ;
} else {
retval = - EINVAL ;
}
return retval ;
}
static struct device_attribute serio_device_attrs [ ] = {
__ATTR ( description , S_IRUGO , serio_show_description , NULL ) ,
2005-06-30 09:48:34 +04:00
__ATTR ( modalias , S_IRUGO , serio_show_modalias , NULL ) ,
2005-04-17 02:20:36 +04:00
__ATTR ( drvctl , S_IWUSR , NULL , serio_rebind_driver ) ,
__ATTR ( bind_mode , S_IWUSR | S_IRUGO , serio_show_bind_mode , serio_set_bind_mode ) ,
__ATTR_NULL
} ;
static void serio_release_port ( struct device * dev )
{
struct serio * serio = to_serio_port ( dev ) ;
kfree ( serio ) ;
module_put ( THIS_MODULE ) ;
}
/*
* Prepare serio port for registration .
*/
static void serio_init_port ( struct serio * serio )
{
static atomic_t serio_no = ATOMIC_INIT ( 0 ) ;
__module_get ( THIS_MODULE ) ;
2006-07-19 09:14:55 +04:00
INIT_LIST_HEAD ( & serio - > node ) ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & serio - > lock ) ;
2006-02-19 08:21:55 +03:00
mutex_init ( & serio - > drv_mutex ) ;
2005-04-17 02:20:36 +04:00
device_initialize ( & serio - > dev ) ;
snprintf ( serio - > dev . bus_id , sizeof ( serio - > dev . bus_id ) ,
" serio%ld " , ( long ) atomic_inc_return ( & serio_no ) - 1 ) ;
serio - > dev . bus = & serio_bus ;
serio - > dev . release = serio_release_port ;
if ( serio - > parent )
serio - > dev . parent = & serio - > parent - > dev ;
}
/*
* Complete serio port registration .
* Driver core will attempt to find appropriate driver for the port .
*/
static void serio_add_port ( struct serio * serio )
{
2006-07-19 09:14:55 +04:00
int error ;
2005-04-17 02:20:36 +04:00
if ( serio - > parent ) {
serio_pause_rx ( serio - > parent ) ;
serio - > parent - > child = serio ;
serio_continue_rx ( serio - > parent ) ;
}
list_add_tail ( & serio - > node , & serio_list ) ;
if ( serio - > start )
serio - > start ( serio ) ;
2006-07-19 09:14:55 +04:00
error = device_add ( & serio - > dev ) ;
if ( error )
printk ( KERN_ERR
" serio: device_add() failed for %s (%s), error: %d \n " ,
serio - > phys , serio - > name , error ) ;
else {
serio - > registered = 1 ;
error = sysfs_create_group ( & serio - > dev . kobj , & serio_id_attr_group ) ;
if ( error )
printk ( KERN_ERR
" serio: sysfs_create_group() failed for %s (%s), error: %d \n " ,
serio - > phys , serio - > name , error ) ;
}
2005-04-17 02:20:36 +04:00
}
/*
* serio_destroy_port ( ) completes deregistration process and removes
* port from the system
*/
static void serio_destroy_port ( struct serio * serio )
{
struct serio * child ;
child = serio_get_pending_child ( serio ) ;
if ( child ) {
serio_remove_pending_events ( child ) ;
put_device ( & child - > dev ) ;
}
if ( serio - > stop )
serio - > stop ( serio ) ;
if ( serio - > parent ) {
serio_pause_rx ( serio - > parent ) ;
serio - > parent - > child = NULL ;
serio_continue_rx ( serio - > parent ) ;
serio - > parent = NULL ;
}
if ( serio - > registered ) {
2005-05-17 08:53:09 +04:00
sysfs_remove_group ( & serio - > dev . kobj , & serio_id_attr_group ) ;
2005-04-17 02:20:36 +04:00
device_del ( & serio - > dev ) ;
serio - > registered = 0 ;
}
2006-07-19 09:14:55 +04:00
list_del_init ( & serio - > node ) ;
2005-04-17 02:20:36 +04:00
serio_remove_pending_events ( serio ) ;
put_device ( & serio - > dev ) ;
}
/*
* Reconnect serio port and all its children ( re - initialize attached devices )
*/
static void serio_reconnect_port ( struct serio * serio )
{
do {
2005-06-28 04:49:45 +04:00
if ( serio_reconnect_driver ( serio ) ) {
2005-04-17 02:20:36 +04:00
serio_disconnect_port ( serio ) ;
serio_find_driver ( serio ) ;
/* Ok, old children are now gone, we are done */
break ;
}
serio = serio - > child ;
} while ( serio ) ;
}
/*
* serio_disconnect_port ( ) unbinds a port from its driver . As a side effect
* all child ports are unbound and destroyed .
*/
static void serio_disconnect_port ( struct serio * serio )
{
struct serio * s , * parent ;
if ( serio - > child ) {
/*
* Children ports should be disconnected and destroyed
* first , staring with the leaf one , since we don ' t want
* to do recursion
*/
for ( s = serio ; s - > child ; s = s - > child )
/* empty */ ;
do {
parent = s - > parent ;
serio_release_driver ( s ) ;
serio_destroy_port ( s ) ;
} while ( ( s = parent ) ! = serio ) ;
}
/*
* Ok , no children left , now disconnect this port
*/
serio_release_driver ( serio ) ;
}
void serio_rescan ( struct serio * serio )
{
serio_queue_event ( serio , NULL , SERIO_RESCAN ) ;
}
void serio_reconnect ( struct serio * serio )
{
serio_queue_event ( serio , NULL , SERIO_RECONNECT ) ;
}
/*
* Submits register request to kseriod for subsequent execution .
* Note that port registration is always asynchronous .
*/
void __serio_register_port ( struct serio * serio , struct module * owner )
{
serio_init_port ( serio ) ;
serio_queue_event ( serio , owner , SERIO_REGISTER_PORT ) ;
}
/*
* Synchronously unregisters serio port .
*/
void serio_unregister_port ( struct serio * serio )
{
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
serio_disconnect_port ( serio ) ;
serio_destroy_port ( serio ) ;
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-28 04:49:45 +04:00
/*
* Safely unregisters child port if one is present .
*/
void serio_unregister_child_port ( struct serio * serio )
{
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio_mutex ) ;
2005-06-28 04:49:45 +04:00
if ( serio - > child ) {
serio_disconnect_port ( serio - > child ) ;
serio_destroy_port ( serio - > child ) ;
}
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio_mutex ) ;
2005-06-28 04:49:45 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Submits register request to kseriod for subsequent execution .
2006-02-19 08:21:55 +03:00
* Can be used when it is not obvious whether the serio_mutex is
2005-04-17 02:20:36 +04:00
* taken or not and when delayed execution is feasible .
*/
void __serio_unregister_port_delayed ( struct serio * serio , struct module * owner )
{
serio_queue_event ( serio , owner , SERIO_UNREGISTER_PORT ) ;
}
/*
* Serio driver operations
*/
static ssize_t serio_driver_show_description ( struct device_driver * drv , char * buf )
{
struct serio_driver * driver = to_serio_driver ( drv ) ;
return sprintf ( buf , " %s \n " , driver - > description ? driver - > description : " (none) " ) ;
}
static ssize_t serio_driver_show_bind_mode ( struct device_driver * drv , char * buf )
{
struct serio_driver * serio_drv = to_serio_driver ( drv ) ;
return sprintf ( buf , " %s \n " , serio_drv - > manual_bind ? " manual " : " auto " ) ;
}
static ssize_t serio_driver_set_bind_mode ( struct device_driver * drv , const char * buf , size_t count )
{
struct serio_driver * serio_drv = to_serio_driver ( drv ) ;
int retval ;
retval = count ;
if ( ! strncmp ( buf , " manual " , count ) ) {
serio_drv - > manual_bind = 1 ;
} else if ( ! strncmp ( buf , " auto " , count ) ) {
serio_drv - > manual_bind = 0 ;
} else {
retval = - EINVAL ;
}
return retval ;
}
static struct driver_attribute serio_driver_attrs [ ] = {
__ATTR ( description , S_IRUGO , serio_driver_show_description , NULL ) ,
__ATTR ( bind_mode , S_IWUSR | S_IRUGO ,
serio_driver_show_bind_mode , serio_driver_set_bind_mode ) ,
__ATTR_NULL
} ;
static int serio_driver_probe ( struct device * dev )
{
struct serio * serio = to_serio_port ( dev ) ;
struct serio_driver * drv = to_serio_driver ( dev - > driver ) ;
2005-06-28 04:49:45 +04:00
return serio_connect_driver ( serio , drv ) ;
2005-04-17 02:20:36 +04:00
}
static int serio_driver_remove ( struct device * dev )
{
struct serio * serio = to_serio_port ( dev ) ;
2005-06-28 04:49:45 +04:00
serio_disconnect_driver ( serio ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-01-05 17:38:53 +03:00
static struct bus_type serio_bus = {
. name = " serio " ,
. probe = serio_driver_probe ,
. remove = serio_driver_remove ,
} ;
2006-07-19 09:14:55 +04:00
static void serio_add_driver ( struct serio_driver * drv )
{
int error ;
error = driver_register ( & drv - > driver ) ;
if ( error )
printk ( KERN_ERR
" serio: driver_register() failed for %s, error: %d \n " ,
drv - > driver . name , error ) ;
}
2005-04-17 02:20:36 +04:00
void __serio_register_driver ( struct serio_driver * drv , struct module * owner )
{
drv - > driver . bus = & serio_bus ;
serio_queue_event ( drv , owner , SERIO_REGISTER_DRIVER ) ;
}
void serio_unregister_driver ( struct serio_driver * drv )
{
struct serio * serio ;
2006-02-19 08:21:55 +03:00
mutex_lock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
drv - > manual_bind = 1 ; /* so serio_find_driver ignores it */
start_over :
list_for_each_entry ( serio , & serio_list , node ) {
if ( serio - > drv = = drv ) {
serio_disconnect_port ( serio ) ;
serio_find_driver ( serio ) ;
/* we could've deleted some ports, restart */
goto start_over ;
}
}
driver_unregister ( & drv - > driver ) ;
2006-02-19 08:21:55 +03:00
mutex_unlock ( & serio_mutex ) ;
2005-04-17 02:20:36 +04:00
}
static void serio_set_drv ( struct serio * serio , struct serio_driver * drv )
{
serio_pause_rx ( serio ) ;
serio - > drv = drv ;
serio_continue_rx ( serio ) ;
}
static int serio_bus_match ( struct device * dev , struct device_driver * drv )
{
struct serio * serio = to_serio_port ( dev ) ;
struct serio_driver * serio_drv = to_serio_driver ( drv ) ;
if ( serio - > manual_bind | | serio_drv - > manual_bind )
return 0 ;
return serio_match_port ( serio_drv - > id_table , serio ) ;
}
# ifdef CONFIG_HOTPLUG
2005-11-16 11:00:00 +03:00
# define SERIO_ADD_UEVENT_VAR(fmt, val...) \
2005-06-30 09:48:34 +04:00
do { \
2005-11-16 11:00:00 +03:00
int err = add_uevent_var ( envp , num_envp , & i , \
2005-06-30 09:48:34 +04:00
buffer , buffer_size , & len , \
fmt , val ) ; \
if ( err ) \
return err ; \
} while ( 0 )
2005-11-16 11:00:00 +03:00
static int serio_uevent ( struct device * dev , char * * envp , int num_envp , char * buffer , int buffer_size )
2005-04-17 02:20:36 +04:00
{
struct serio * serio ;
int i = 0 ;
2005-06-30 09:48:34 +04:00
int len = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
return - ENODEV ;
serio = to_serio_port ( dev ) ;
2005-11-16 11:00:00 +03:00
SERIO_ADD_UEVENT_VAR ( " SERIO_TYPE=%02x " , serio - > id . type ) ;
SERIO_ADD_UEVENT_VAR ( " SERIO_PROTO=%02x " , serio - > id . proto ) ;
SERIO_ADD_UEVENT_VAR ( " SERIO_ID=%02x " , serio - > id . id ) ;
SERIO_ADD_UEVENT_VAR ( " SERIO_EXTRA=%02x " , serio - > id . extra ) ;
SERIO_ADD_UEVENT_VAR ( " MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X " ,
2005-06-30 09:48:34 +04:00
serio - > id . type , serio - > id . proto , serio - > id . id , serio - > id . extra ) ;
2005-04-17 02:20:36 +04:00
envp [ i ] = NULL ;
return 0 ;
}
2005-11-16 11:00:00 +03:00
# undef SERIO_ADD_UEVENT_VAR
2005-04-17 02:20:36 +04:00
# else
2005-11-16 11:00:00 +03:00
static int serio_uevent ( struct device * dev , char * * envp , int num_envp , char * buffer , int buffer_size )
2005-04-17 02:20:36 +04:00
{
return - ENODEV ;
}
# endif /* CONFIG_HOTPLUG */
static int serio_resume ( struct device * dev )
{
struct serio * serio = to_serio_port ( dev ) ;
2005-06-28 04:49:45 +04:00
if ( serio_reconnect_driver ( serio ) ) {
2005-04-17 02:20:36 +04:00
/*
* Driver re - probing can take a while , so better let kseriod
* deal with it .
*/
serio_rescan ( serio ) ;
}
return 0 ;
}
2006-02-19 08:21:55 +03:00
/* called from serio_driver->connect/disconnect methods under serio_mutex */
2005-04-17 02:20:36 +04:00
int serio_open ( struct serio * serio , struct serio_driver * drv )
{
serio_set_drv ( serio , drv ) ;
if ( serio - > open & & serio - > open ( serio ) ) {
serio_set_drv ( serio , NULL ) ;
return - 1 ;
}
return 0 ;
}
2006-02-19 08:21:55 +03:00
/* called from serio_driver->connect/disconnect methods under serio_mutex */
2005-04-17 02:20:36 +04:00
void serio_close ( struct serio * serio )
{
if ( serio - > close )
serio - > close ( serio ) ;
serio_set_drv ( serio , NULL ) ;
}
irqreturn_t serio_interrupt ( struct serio * serio ,
unsigned char data , unsigned int dfl , struct pt_regs * regs )
{
unsigned long flags ;
irqreturn_t ret = IRQ_NONE ;
spin_lock_irqsave ( & serio - > lock , flags ) ;
if ( likely ( serio - > drv ) ) {
ret = serio - > drv - > interrupt ( serio , data , dfl , regs ) ;
} else if ( ! dfl & & serio - > registered ) {
serio_rescan ( serio ) ;
ret = IRQ_HANDLED ;
}
spin_unlock_irqrestore ( & serio - > lock , flags ) ;
return ret ;
}
static int __init serio_init ( void )
{
2006-07-19 09:14:55 +04:00
int error ;
2005-04-17 02:20:36 +04:00
serio_bus . dev_attrs = serio_device_attrs ;
serio_bus . drv_attrs = serio_driver_attrs ;
serio_bus . match = serio_bus_match ;
2005-11-16 11:00:00 +03:00
serio_bus . uevent = serio_uevent ;
2005-04-17 02:20:36 +04:00
serio_bus . resume = serio_resume ;
2006-07-19 09:14:55 +04:00
error = bus_register ( & serio_bus ) ;
if ( error ) {
printk ( KERN_ERR " serio: failed to register serio bus, error: %d \n " , error ) ;
return error ;
}
serio_task = kthread_run ( serio_thread , NULL , " kseriod " ) ;
if ( IS_ERR ( serio_task ) ) {
bus_unregister ( & serio_bus ) ;
error = PTR_ERR ( serio_task ) ;
printk ( KERN_ERR " serio: Failed to start kseriod, error: %d \n " , error ) ;
return error ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit serio_exit ( void )
{
bus_unregister ( & serio_bus ) ;
2005-06-28 04:49:45 +04:00
kthread_stop ( serio_task ) ;
2005-04-17 02:20:36 +04:00
}
2006-02-19 08:22:51 +03:00
subsys_initcall ( serio_init ) ;
2005-04-17 02:20:36 +04:00
module_exit ( serio_exit ) ;