2005-04-16 15:20:36 -07:00
/*
* IUCV special message driver
*
* Copyright ( C ) 2003 IBM Deutschland Entwicklung GmbH , IBM Corporation
* 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>
# include <asm/cpcmd.h>
# include <asm/ebcdic.h>
# include "iucv.h"
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 " ) ;
static iucv_handle_t smsg_handle ;
static unsigned short smsg_pathid ;
static DEFINE_SPINLOCK ( smsg_list_lock ) ;
static struct list_head smsg_list = LIST_HEAD_INIT ( smsg_list ) ;
static void
smsg_connection_complete ( iucv_ConnectionComplete * eib , void * pgm_data )
{
}
static void
smsg_message_pending ( iucv_MessagePending * eib , void * pgm_data )
{
struct smsg_callback * cb ;
unsigned char * msg ;
2005-05-01 08:58:58 -07:00
unsigned char sender [ 9 ] ;
2005-04-16 15:20:36 -07:00
unsigned short len ;
2005-05-01 08:58:58 -07:00
int rc , i ;
2005-04-16 15:20:36 -07:00
len = eib - > ln1msg2 . ipbfln1f ;
msg = kmalloc ( len + 1 , GFP_ATOMIC | GFP_DMA ) ;
if ( ! msg ) {
iucv_reject ( eib - > ippathid , eib - > ipmsgid , eib - > iptrgcls ) ;
return ;
}
rc = iucv_receive ( eib - > ippathid , eib - > ipmsgid , eib - > iptrgcls ,
msg , len , 0 , 0 , 0 ) ;
if ( rc = = 0 ) {
msg [ len ] = 0 ;
EBCASC ( msg , len ) ;
2005-05-01 08:58:58 -07:00
memcpy ( sender , msg , 8 ) ;
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 )
if ( strncmp ( msg + 8 , cb - > prefix , cb - > len ) = = 0 ) {
2005-05-01 08:58:58 -07:00
cb - > callback ( sender , msg + 8 ) ;
2005-04-16 15:20:36 -07:00
break ;
}
spin_unlock ( & smsg_list_lock ) ;
}
kfree ( msg ) ;
}
static iucv_interrupt_ops_t smsg_ops = {
. ConnectionComplete = smsg_connection_complete ,
. MessagePending = smsg_message_pending ,
} ;
static struct device_driver smsg_driver = {
. name = " SMSGIUCV " ,
. bus = & iucv_bus ,
} ;
int
2005-05-01 08:58:58 -07:00
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 ;
spin_lock ( & smsg_list_lock ) ;
list_add_tail ( & cb - > list , & smsg_list ) ;
spin_unlock ( & smsg_list_lock ) ;
return 0 ;
}
void
2005-05-01 08:58:58 -07:00
smsg_unregister_callback ( char * prefix , void ( * callback ) ( char * from , char * str ) )
2005-04-16 15:20:36 -07:00
{
struct smsg_callback * cb , * tmp ;
spin_lock ( & smsg_list_lock ) ;
cb = 0 ;
list_for_each_entry ( tmp , & smsg_list , list )
if ( tmp - > callback = = callback & &
strcmp ( tmp - > prefix , prefix ) = = 0 ) {
cb = tmp ;
list_del ( & cb - > list ) ;
break ;
}
spin_unlock ( & smsg_list_lock ) ;
kfree ( cb ) ;
}
static void __exit
smsg_exit ( void )
{
if ( smsg_handle > 0 ) {
2005-06-25 14:55:32 -07:00
cpcmd ( " SET SMSG OFF " , NULL , 0 , NULL ) ;
2005-04-16 15:20:36 -07:00
iucv_sever ( smsg_pathid , 0 ) ;
iucv_unregister_program ( smsg_handle ) ;
driver_unregister ( & smsg_driver ) ;
}
return ;
}
static int __init
smsg_init ( void )
{
static unsigned char pgmmask [ 24 ] = {
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff
} ;
int rc ;
rc = driver_register ( & smsg_driver ) ;
if ( rc ! = 0 ) {
printk ( KERN_ERR " SMSGIUCV: failed to register driver. \n " ) ;
return rc ;
}
smsg_handle = iucv_register_program ( " SMSGIUCV " , " *MSG " ,
pgmmask , & smsg_ops , 0 ) ;
if ( ! smsg_handle ) {
printk ( KERN_ERR " SMSGIUCV: failed to register to iucv " ) ;
driver_unregister ( & smsg_driver ) ;
return - EIO ; /* better errno ? */
}
2006-03-07 21:55:38 -08:00
rc = iucv_connect ( & smsg_pathid , 255 , 0 , " *MSG " , 0 , 0 , 0 , 0 ,
2005-04-16 15:20:36 -07:00
smsg_handle , 0 ) ;
if ( rc ) {
printk ( KERN_ERR " SMSGIUCV: failed to connect to *MSG " ) ;
iucv_unregister_program ( smsg_handle ) ;
driver_unregister ( & smsg_driver ) ;
smsg_handle = 0 ;
return - EIO ;
}
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 ;
}
module_init ( smsg_init ) ;
module_exit ( smsg_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
EXPORT_SYMBOL ( smsg_register_callback ) ;
EXPORT_SYMBOL ( smsg_unregister_callback ) ;