2005-04-16 15:20:36 -07:00
/*
* IUCV special message driver
*
2007-02-08 13:51:11 -08:00
* Copyright 2003 IBM Deutschland Entwicklung GmbH , IBM Corporation
2005-04-16 15:20:36 -07: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-08 13:51:11 -08:00
# include <net/iucv/iucv.h>
2005-04-16 15:20:36 -07:00
# include <asm/cpcmd.h>
# include <asm/ebcdic.h>
2007-02-08 13:51:11 -08:00
# include "smsgiucv.h"
2005-04-16 15:20:36 -07:00
struct smsg_callback {
struct list_head list ;
char * prefix ;
int len ;
2005-05-01 08:58:58 -07:00
void ( * callback ) ( char * from , char * str ) ;
2005-04-16 15:20:36 -07: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-08 13:51:11 -08:00
static struct iucv_path * smsg_path ;
2005-04-16 15:20:36 -07:00
static DEFINE_SPINLOCK ( smsg_list_lock ) ;
static struct list_head smsg_list = LIST_HEAD_INIT ( smsg_list ) ;
2007-02-08 13:51:11 -08: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-16 15:20:36 -07:00
{
2007-02-08 13:51:11 -08: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-16 15:20:36 -07:00
}
2007-02-08 13:51:11 -08:00
static void smsg_message_pending ( struct iucv_path * path ,
struct iucv_message * msg )
2005-04-16 15:20:36 -07:00
{
struct smsg_callback * cb ;
2007-02-08 13:51:11 -08:00
unsigned char * buffer ;
2005-05-01 08:58:58 -07:00
unsigned char sender [ 9 ] ;
int rc , i ;
2005-04-16 15:20:36 -07:00
2007-02-08 13:51:11 -08:00
buffer = kmalloc ( msg - > length + 1 , GFP_ATOMIC | GFP_DMA ) ;
if ( ! buffer ) {
iucv_message_reject ( path , msg ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2007-02-08 13:51:11 -08:00
rc = iucv_message_receive ( path , msg , 0 , buffer , msg - > length , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( rc = = 0 ) {
2007-02-08 13:51:11 -08:00
buffer [ msg - > length ] = 0 ;
EBCASC ( buffer , msg - > length ) ;
memcpy ( sender , buffer , 8 ) ;
2005-05-01 08:58:58 -07: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-16 15:20:36 -07:00
spin_lock ( & smsg_list_lock ) ;
list_for_each_entry ( cb , & smsg_list , list )
2007-02-08 13:51:11 -08:00
if ( strncmp ( buffer + 8 , cb - > prefix , cb - > len ) = = 0 ) {
cb - > callback ( sender , buffer + 8 ) ;
2005-04-16 15:20:36 -07:00
break ;
}
spin_unlock ( & smsg_list_lock ) ;
}
2007-02-08 13:51:11 -08:00
kfree ( buffer ) ;
2005-04-16 15:20:36 -07:00
}
2007-02-08 13:51:11 -08:00
int smsg_register_callback ( char * prefix ,
void ( * callback ) ( char * from , char * str ) )
2005-04-16 15:20:36 -07: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-08 13:51:11 -08:00
spin_lock_bh ( & smsg_list_lock ) ;
2005-04-16 15:20:36 -07:00
list_add_tail ( & cb - > list , & smsg_list ) ;
2007-02-08 13:51:11 -08:00
spin_unlock_bh ( & smsg_list_lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2007-02-08 13:51:11 -08:00
void smsg_unregister_callback ( char * prefix ,
void ( * callback ) ( char * from , char * str ) )
2005-04-16 15:20:36 -07:00
{
struct smsg_callback * cb , * tmp ;
2007-02-08 13:51:11 -08:00
spin_lock_bh ( & smsg_list_lock ) ;
2006-07-12 16:41:55 +02:00
cb = NULL ;
2005-04-16 15:20:36 -07: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-08 13:51:11 -08:00
spin_unlock_bh ( & smsg_list_lock ) ;
2005-04-16 15:20:36 -07:00
kfree ( cb ) ;
}
2007-02-08 13:51:11 -08:00
static struct device_driver smsg_driver = {
. name = " SMSGIUCV " ,
. bus = & iucv_bus ,
} ;
static void __exit smsg_exit ( void )
2005-04-16 15:20:36 -07:00
{
2007-02-08 13:51:11 -08:00
cpcmd ( " SET SMSG IUCV " , NULL , 0 , NULL ) ;
iucv_unregister ( & smsg_handler , 1 ) ;
driver_unregister ( & smsg_driver ) ;
2005-04-16 15:20:36 -07:00
}
2007-02-08 13:51:11 -08:00
static int __init smsg_init ( void )
2005-04-16 15:20:36 -07:00
{
int rc ;
rc = driver_register ( & smsg_driver ) ;
2007-02-08 13:51:11 -08:00
if ( rc ! = 0 )
goto out ;
rc = iucv_register ( & smsg_handler , 1 ) ;
if ( rc ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " SMSGIUCV: failed to register to iucv " ) ;
2007-02-08 13:51:11 -08:00
rc = - EIO ; /* better errno ? */
goto out_driver ;
}
smsg_path = iucv_path_alloc ( 255 , 0 , GFP_KERNEL ) ;
if ( ! smsg_path ) {
rc = - ENOMEM ;
goto out_register ;
2005-04-16 15:20:36 -07:00
}
2007-02-08 13:51:11 -08:00
rc = iucv_path_connect ( smsg_path , & smsg_handler , " *MSG " ,
NULL , NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( rc ) {
printk ( KERN_ERR " SMSGIUCV: failed to connect to *MSG " ) ;
2007-02-08 13:51:11 -08:00
rc = - EIO ; /* better errno ? */
goto out_free ;
2005-04-16 15:20:36 -07:00
}
2005-06-25 14:55:32 -07:00
cpcmd ( " SET SMSG IUCV " , NULL , 0 , NULL ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
2007-02-08 13:51:11 -08:00
out_free :
iucv_path_free ( smsg_path ) ;
out_register :
iucv_unregister ( & smsg_handler , 1 ) ;
out_driver :
driver_unregister ( & smsg_driver ) ;
out :
return rc ;
2005-04-16 15:20:36 -07:00
}
module_init ( smsg_init ) ;
module_exit ( smsg_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
EXPORT_SYMBOL ( smsg_register_callback ) ;
EXPORT_SYMBOL ( smsg_unregister_callback ) ;