2005-04-17 02:20:36 +04:00
/*
* IUCV special message driver
*
2009-06-16 12:30:46 +04:00
* Copyright IBM Corp . 2003 , 2009
*
2005-04-17 02:20:36 +04:00
* Author ( s ) : Martin Schwidefsky ( schwidefsky @ de . ibm . com )
*
* 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 , 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/device.h>
2007-02-09 00:51:11 +03:00
# include <net/iucv/iucv.h>
2005-04-17 02:20:36 +04:00
# include <asm/cpcmd.h>
# include <asm/ebcdic.h>
2007-02-09 00:51:11 +03:00
# include "smsgiucv.h"
2005-04-17 02:20:36 +04:00
struct smsg_callback {
struct list_head list ;
char * prefix ;
int len ;
2005-05-01 19:58:58 +04:00
void ( * callback ) ( char * from , char * str ) ;
2005-04-17 02:20:36 +04:00
} ;
MODULE_AUTHOR
( " (C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com) " ) ;
MODULE_DESCRIPTION ( " Linux for S/390 IUCV special message driver " ) ;
2007-02-09 00:51:11 +03:00
static struct iucv_path * smsg_path ;
2009-06-16 12:30:46 +04:00
/* dummy device used as trigger for PM functions */
static struct device * smsg_dev ;
2007-02-09 00:51:11 +03:00
2005-04-17 02:20:36 +04:00
static DEFINE_SPINLOCK ( smsg_list_lock ) ;
2008-01-26 16:11:13 +03:00
static LIST_HEAD ( smsg_list ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:51:11 +03:00
static int smsg_path_pending ( struct iucv_path * , u8 ipvmid [ 8 ] , u8 ipuser [ 16 ] ) ;
static void smsg_message_pending ( struct iucv_path * , struct iucv_message * ) ;
static struct iucv_handler smsg_handler = {
. path_pending = smsg_path_pending ,
. message_pending = smsg_message_pending ,
} ;
static int smsg_path_pending ( struct iucv_path * path , u8 ipvmid [ 8 ] ,
u8 ipuser [ 16 ] )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:51:11 +03:00
if ( strncmp ( ipvmid , " *MSG " , sizeof ( ipvmid ) ) ! = 0 )
return - EINVAL ;
/* Path pending from *MSG. */
return iucv_path_accept ( path , & smsg_handler , " SMSGIUCV " , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:51:11 +03:00
static void smsg_message_pending ( struct iucv_path * path ,
struct iucv_message * msg )
2005-04-17 02:20:36 +04:00
{
struct smsg_callback * cb ;
2007-02-09 00:51:11 +03:00
unsigned char * buffer ;
2005-05-01 19:58:58 +04:00
unsigned char sender [ 9 ] ;
int rc , i ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:51:11 +03:00
buffer = kmalloc ( msg - > length + 1 , GFP_ATOMIC | GFP_DMA ) ;
if ( ! buffer ) {
iucv_message_reject ( path , msg ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-02-09 00:51:11 +03:00
rc = iucv_message_receive ( path , msg , 0 , buffer , msg - > length , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( rc = = 0 ) {
2007-02-09 00:51:11 +03:00
buffer [ msg - > length ] = 0 ;
EBCASC ( buffer , msg - > length ) ;
memcpy ( sender , buffer , 8 ) ;
2005-05-01 19:58:58 +04:00
sender [ 8 ] = 0 ;
/* Remove trailing whitespace from the sender name. */
for ( i = 7 ; i > = 0 ; i - - ) {
if ( sender [ i ] ! = ' ' & & sender [ i ] ! = ' \t ' )
break ;
sender [ i ] = 0 ;
}
2005-04-17 02:20:36 +04:00
spin_lock ( & smsg_list_lock ) ;
list_for_each_entry ( cb , & smsg_list , list )
2007-02-09 00:51:11 +03:00
if ( strncmp ( buffer + 8 , cb - > prefix , cb - > len ) = = 0 ) {
cb - > callback ( sender , buffer + 8 ) ;
2005-04-17 02:20:36 +04:00
break ;
}
spin_unlock ( & smsg_list_lock ) ;
}
2007-02-09 00:51:11 +03:00
kfree ( buffer ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:51:11 +03:00
int smsg_register_callback ( char * prefix ,
void ( * callback ) ( char * from , char * str ) )
2005-04-17 02:20:36 +04:00
{
struct smsg_callback * cb ;
cb = kmalloc ( sizeof ( struct smsg_callback ) , GFP_KERNEL ) ;
if ( ! cb )
return - ENOMEM ;
cb - > prefix = prefix ;
cb - > len = strlen ( prefix ) ;
cb - > callback = callback ;
2007-02-09 00:51:11 +03:00
spin_lock_bh ( & smsg_list_lock ) ;
2005-04-17 02:20:36 +04:00
list_add_tail ( & cb - > list , & smsg_list ) ;
2007-02-09 00:51:11 +03:00
spin_unlock_bh ( & smsg_list_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-09 00:51:11 +03:00
void smsg_unregister_callback ( char * prefix ,
void ( * callback ) ( char * from , char * str ) )
2005-04-17 02:20:36 +04:00
{
struct smsg_callback * cb , * tmp ;
2007-02-09 00:51:11 +03:00
spin_lock_bh ( & smsg_list_lock ) ;
2006-07-12 18:41:55 +04:00
cb = NULL ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( tmp , & smsg_list , list )
if ( tmp - > callback = = callback & &
strcmp ( tmp - > prefix , prefix ) = = 0 ) {
cb = tmp ;
list_del ( & cb - > list ) ;
break ;
}
2007-02-09 00:51:11 +03:00
spin_unlock_bh ( & smsg_list_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( cb ) ;
}
2009-06-16 12:30:46 +04:00
static int smsg_pm_freeze ( struct device * dev )
{
# ifdef CONFIG_PM_DEBUG
printk ( KERN_WARNING " smsg_pm_freeze \n " ) ;
# endif
if ( smsg_path )
iucv_path_sever ( smsg_path , NULL ) ;
return 0 ;
}
static int smsg_pm_restore_thaw ( struct device * dev )
{
int rc ;
# ifdef CONFIG_PM_DEBUG
printk ( KERN_WARNING " smsg_pm_restore_thaw \n " ) ;
# endif
if ( smsg_path ) {
memset ( smsg_path , 0 , sizeof ( * smsg_path ) ) ;
smsg_path - > msglim = 255 ;
smsg_path - > flags = 0 ;
rc = iucv_path_connect ( smsg_path , & smsg_handler , " *MSG " ,
NULL , NULL , NULL ) ;
printk ( KERN_ERR " iucv_path_connect returned with rc %i \n " , rc ) ;
}
return 0 ;
}
static struct dev_pm_ops smsg_pm_ops = {
. freeze = smsg_pm_freeze ,
. thaw = smsg_pm_restore_thaw ,
. restore = smsg_pm_restore_thaw ,
} ;
2007-02-09 00:51:11 +03:00
static struct device_driver smsg_driver = {
2009-06-16 12:30:46 +04:00
. owner = THIS_MODULE ,
2007-02-09 00:51:11 +03:00
. name = " SMSGIUCV " ,
. bus = & iucv_bus ,
2009-06-16 12:30:46 +04:00
. pm = & smsg_pm_ops ,
2007-02-09 00:51:11 +03:00
} ;
static void __exit smsg_exit ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:51:11 +03:00
cpcmd ( " SET SMSG IUCV " , NULL , 0 , NULL ) ;
2009-06-16 12:30:46 +04:00
device_unregister ( smsg_dev ) ;
2007-02-09 00:51:11 +03:00
iucv_unregister ( & smsg_handler , 1 ) ;
driver_unregister ( & smsg_driver ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:51:11 +03:00
static int __init smsg_init ( void )
2005-04-17 02:20:36 +04:00
{
int rc ;
2007-11-05 13:10:08 +03:00
if ( ! MACHINE_IS_VM ) {
rc = - EPROTONOSUPPORT ;
goto out ;
}
2005-04-17 02:20:36 +04:00
rc = driver_register ( & smsg_driver ) ;
2007-02-09 00:51:11 +03:00
if ( rc ! = 0 )
goto out ;
rc = iucv_register ( & smsg_handler , 1 ) ;
2008-07-14 11:59:33 +04:00
if ( rc )
2007-02-09 00:51:11 +03:00
goto out_driver ;
smsg_path = iucv_path_alloc ( 255 , 0 , GFP_KERNEL ) ;
if ( ! smsg_path ) {
rc = - ENOMEM ;
goto out_register ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:51:11 +03:00
rc = iucv_path_connect ( smsg_path , & smsg_handler , " *MSG " ,
NULL , NULL , NULL ) ;
2008-07-14 11:59:33 +04:00
if ( rc )
2009-06-16 12:30:46 +04:00
goto out_free_path ;
smsg_dev = kzalloc ( sizeof ( struct device ) , GFP_KERNEL ) ;
if ( ! smsg_dev ) {
rc = - ENOMEM ;
goto out_free_path ;
}
dev_set_name ( smsg_dev , " smsg_iucv " ) ;
smsg_dev - > bus = & iucv_bus ;
smsg_dev - > parent = iucv_root ;
smsg_dev - > release = ( void ( * ) ( struct device * ) ) kfree ;
smsg_dev - > driver = & smsg_driver ;
rc = device_register ( smsg_dev ) ;
if ( rc )
2009-09-11 12:28:38 +04:00
goto out_put ;
2009-06-16 12:30:46 +04:00
2005-06-26 01:55:32 +04:00
cpcmd ( " SET SMSG IUCV " , NULL , 0 , NULL ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2007-02-09 00:51:11 +03:00
2009-09-11 12:28:38 +04:00
out_put :
put_device ( smsg_dev ) ;
2009-06-16 12:30:46 +04:00
out_free_path :
2007-02-09 00:51:11 +03:00
iucv_path_free ( smsg_path ) ;
2009-06-16 12:30:46 +04:00
smsg_path = NULL ;
2007-02-09 00:51:11 +03:00
out_register :
iucv_unregister ( & smsg_handler , 1 ) ;
out_driver :
driver_unregister ( & smsg_driver ) ;
out :
return rc ;
2005-04-17 02:20:36 +04:00
}
module_init ( smsg_init ) ;
module_exit ( smsg_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
EXPORT_SYMBOL ( smsg_register_callback ) ;
EXPORT_SYMBOL ( smsg_unregister_callback ) ;