2006-05-27 05:58:38 +04:00
/*
2005-04-17 02:20:36 +04:00
* IUCV network driver
*
* Copyright ( C ) 2001 IBM Deutschland Entwicklung GmbH , IBM Corporation
* Author ( s ) :
* Original source :
* Alan Altmark ( Alan_Altmark @ us . ibm . com ) Sept . 2000
* Xenia Tkatschow ( xenia @ us . ibm . com )
* 2 Gb awareness and general cleanup :
* Fritz Elfert ( elfert @ de . ibm . com , felfert @ millenux . com )
*
* Documentation used :
* The original source
* CP Programming Service , IBM document # SC24 - 5760
*
* 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 .
*
*/
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
/* #define DEBUG */
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/device.h>
# include <asm/atomic.h>
# include "iucv.h"
# include <asm/io.h>
# include <asm/s390_ext.h>
# include <asm/ebcdic.h>
# include <asm/smp.h>
2006-01-06 11:19:14 +03:00
# include <asm/s390_rdev.h>
2005-04-17 02:20:36 +04:00
/* FLAGS:
* All flags are defined in the field IPFLAGS1 of each function
* and can be found in CP Programming Services .
* IPSRCCLS - Indicates you have specified a source class
* IPFGMCL - Indicates you have specified a target class
* IPFGPID - Indicates you have specified a pathid
* IPFGMID - Indicates you have specified a message ID
* IPANSLST - Indicates that you are using an address list for
* reply data
* IPBUFLST - Indicates that you are using an address list for
* message data
*/
# define IPSRCCLS 0x01
# define IPFGMCL 0x01
# define IPFGPID 0x02
# define IPFGMID 0x04
# define IPANSLST 0x08
# define IPBUFLST 0x40
static int
iucv_bus_match ( struct device * dev , struct device_driver * drv )
{
return 0 ;
}
struct bus_type iucv_bus = {
. name = " iucv " ,
. match = iucv_bus_match ,
2006-05-27 05:58:38 +04:00
} ;
2005-04-17 02:20:36 +04:00
struct device * iucv_root ;
/* General IUCV interrupt structure */
typedef struct {
__u16 ippathid ;
__u8 res1 ;
__u8 iptype ;
__u32 res2 ;
__u8 ipvmid [ 8 ] ;
__u8 res3 [ 24 ] ;
} iucv_GeneralInterrupt ;
static iucv_GeneralInterrupt * iucv_external_int_buffer = NULL ;
/* Spin Lock declaration */
static DEFINE_SPINLOCK ( iucv_lock ) ;
static int messagesDisabled = 0 ;
/***************INTERRUPT HANDLING ***************/
typedef struct {
struct list_head queue ;
iucv_GeneralInterrupt data ;
} iucv_irqdata ;
static struct list_head iucv_irq_queue ;
static DEFINE_SPINLOCK ( iucv_irq_queue_lock ) ;
/*
* Internal function prototypes
*/
static void iucv_tasklet_handler ( unsigned long ) ;
static void iucv_irq_handler ( struct pt_regs * , __u16 ) ;
static DECLARE_TASKLET ( iucv_tasklet , iucv_tasklet_handler , 0 ) ;
/************ FUNCTION ID'S ****************************/
# define ACCEPT 10
# define CONNECT 11
# define DECLARE_BUFFER 12
# define PURGE 9
# define QUERY 0
# define QUIESCE 13
# define RECEIVE 5
# define REJECT 8
# define REPLY 6
# define RESUME 14
# define RETRIEVE_BUFFER 2
# define SEND 4
# define SETMASK 16
# define SEVER 15
/**
* Structure : handler
* members : list - list management .
* structure : id
* userid - 8 char array of machine identification
* user_data - 16 char array for user identification
* mask - 24 char array used to compare the 2 previous
* interrupt_table - vector of interrupt functions .
* pgm_data - ulong , application data that is passed
* to the interrupt handlers
*/
typedef struct handler_t {
struct list_head list ;
struct {
__u8 userid [ 8 ] ;
__u8 user_data [ 16 ] ;
__u8 mask [ 24 ] ;
} id ;
iucv_interrupt_ops_t * interrupt_table ;
void * pgm_data ;
} handler ;
/**
* iucv_handler_table : List of registered handlers .
*/
static struct list_head iucv_handler_table ;
/**
* iucv_pathid_table : an array of * handler pointing into
* iucv_handler_table for fast indexing by pathid ;
*/
static handler * * iucv_pathid_table ;
static unsigned long max_connections ;
/**
* iucv_cpuid : contains the logical cpu number of the cpu which
* has declared the iucv buffer by issuing DECLARE_BUFFER .
* If no cpu has done the initialization iucv_cpuid contains - 1.
*/
static int iucv_cpuid = - 1 ;
/**
* register_flag : is 0 when external interrupt has not been registered
*/
static int register_flag ;
/****************FIVE 40-BYTE PARAMETER STRUCTURES******************/
/* Data struct 1: iparml_control
* Used for iucv_accept
* iucv_connect
* iucv_quiesce
* iucv_resume
* iucv_sever
* iucv_retrieve_buffer
* Data struct 2 : iparml_dpl ( data in parameter list )
* Used for iucv_send_prmmsg
* iucv_send2way_prmmsg
* iucv_send2way_prmmsg_array
* iucv_reply_prmmsg
* Data struct 3 : iparml_db ( data in a buffer )
* Used for iucv_receive
* iucv_receive_array
* iucv_reject
* iucv_reply
* iucv_reply_array
* iucv_send
* iucv_send_array
* iucv_send2way
* iucv_send2way_array
* iucv_declare_buffer
* Data struct 4 : iparml_purge
* Used for iucv_purge
* iucv_query
* Data struct 5 : iparml_set_mask
* Used for iucv_set_mask
*/
typedef struct {
__u16 ippathid ;
__u8 ipflags1 ;
__u8 iprcode ;
__u16 ipmsglim ;
__u16 res1 ;
__u8 ipvmid [ 8 ] ;
__u8 ipuser [ 16 ] ;
__u8 iptarget [ 8 ] ;
} iparml_control ;
typedef struct {
__u16 ippathid ;
__u8 ipflags1 ;
__u8 iprcode ;
__u32 ipmsgid ;
__u32 iptrgcls ;
__u8 iprmmsg [ 8 ] ;
__u32 ipsrccls ;
__u32 ipmsgtag ;
__u32 ipbfadr2 ;
__u32 ipbfln2f ;
__u32 res ;
} iparml_dpl ;
typedef struct {
__u16 ippathid ;
__u8 ipflags1 ;
__u8 iprcode ;
__u32 ipmsgid ;
__u32 iptrgcls ;
__u32 ipbfadr1 ;
__u32 ipbfln1f ;
__u32 ipsrccls ;
__u32 ipmsgtag ;
__u32 ipbfadr2 ;
__u32 ipbfln2f ;
__u32 res ;
} iparml_db ;
typedef struct {
__u16 ippathid ;
__u8 ipflags1 ;
__u8 iprcode ;
__u32 ipmsgid ;
__u8 ipaudit [ 3 ] ;
__u8 res1 [ 5 ] ;
__u32 res2 ;
__u32 ipsrccls ;
__u32 ipmsgtag ;
__u32 res3 [ 3 ] ;
} iparml_purge ;
typedef struct {
__u8 ipmask ;
__u8 res1 [ 2 ] ;
__u8 iprcode ;
__u32 res2 [ 9 ] ;
} iparml_set_mask ;
typedef struct {
union {
iparml_control p_ctrl ;
iparml_dpl p_dpl ;
iparml_db p_db ;
iparml_purge p_purge ;
iparml_set_mask p_set_mask ;
} param ;
atomic_t in_use ;
__u32 res ;
} __attribute__ ( ( aligned ( 8 ) ) ) iucv_param ;
# define PARAM_POOL_SIZE (PAGE_SIZE / sizeof(iucv_param))
static iucv_param * iucv_param_pool ;
MODULE_AUTHOR ( " (C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com) " ) ;
MODULE_DESCRIPTION ( " Linux for S/390 IUCV lowlevel driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Debugging stuff
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
static int debuglevel = 0 ;
module_param ( debuglevel , int , 0 ) ;
MODULE_PARM_DESC ( debuglevel ,
" Specifies the debug level (0=off ... 3=all) " ) ;
static void
iucv_dumpit ( char * title , void * buf , int len )
{
int i ;
__u8 * p = ( __u8 * ) buf ;
if ( debuglevel < 3 )
return ;
printk ( KERN_DEBUG " %s \n " , title ) ;
printk ( " " ) ;
for ( i = 0 ; i < len ; i + + ) {
if ( ! ( i % 16 ) & & i ! = 0 )
printk ( " \n " ) ;
else if ( ! ( i % 4 ) & & i ! = 0 )
printk ( " " ) ;
printk ( " %02X " , * p + + ) ;
}
if ( len % 16 )
printk ( " \n " ) ;
return ;
}
# define iucv_debug(lvl, fmt, args...) \
do { \
if ( debuglevel > = lvl ) \
printk ( KERN_DEBUG " %s: " fmt " \n " , __FUNCTION__ , # # args ) ; \
} while ( 0 )
# else
# define iucv_debug(lvl, fmt, args...)
# define iucv_dumpit(title, buf, len)
# endif
/*
* Internal functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
/**
* print start banner
*/
static void
iucv_banner ( void )
{
2006-02-01 14:06:31 +03:00
printk ( KERN_INFO " IUCV lowlevel driver initialized \n " ) ;
2005-04-17 02:20:36 +04:00
}
/**
* iucv_init - Initialization
*
* Allocates and initializes various data structures .
*/
static int
iucv_init ( void )
{
int ret ;
if ( iucv_external_int_buffer )
return 0 ;
if ( ! MACHINE_IS_VM ) {
printk ( KERN_ERR " IUCV: IUCV connection needs VM as base \n " ) ;
return - EPROTONOSUPPORT ;
}
ret = bus_register ( & iucv_bus ) ;
if ( ret ) {
printk ( KERN_ERR " IUCV: failed to register bus. \n " ) ;
return ret ;
}
iucv_root = s390_root_dev_register ( " iucv " ) ;
if ( IS_ERR ( iucv_root ) ) {
printk ( KERN_ERR " IUCV: failed to register iucv root. \n " ) ;
bus_unregister ( & iucv_bus ) ;
return PTR_ERR ( iucv_root ) ;
}
/* Note: GFP_DMA used used to get memory below 2G */
2006-03-24 14:15:31 +03:00
iucv_external_int_buffer = kzalloc ( sizeof ( iucv_GeneralInterrupt ) ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL | GFP_DMA ) ;
if ( ! iucv_external_int_buffer ) {
printk ( KERN_WARNING
" %s: Could not allocate external interrupt buffer \n " ,
__FUNCTION__ ) ;
s390_root_dev_unregister ( iucv_root ) ;
bus_unregister ( & iucv_bus ) ;
return - ENOMEM ;
}
/* Initialize parameter pool */
2006-03-24 14:15:31 +03:00
iucv_param_pool = kzalloc ( sizeof ( iucv_param ) * PARAM_POOL_SIZE ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL | GFP_DMA ) ;
if ( ! iucv_param_pool ) {
printk ( KERN_WARNING " %s: Could not allocate param pool \n " ,
__FUNCTION__ ) ;
kfree ( iucv_external_int_buffer ) ;
iucv_external_int_buffer = NULL ;
s390_root_dev_unregister ( iucv_root ) ;
bus_unregister ( & iucv_bus ) ;
return - ENOMEM ;
}
/* Initialize irq queue */
INIT_LIST_HEAD ( & iucv_irq_queue ) ;
/* Initialize handler table */
INIT_LIST_HEAD ( & iucv_handler_table ) ;
iucv_banner ( ) ;
return 0 ;
}
/**
* iucv_exit - De - Initialization
*
* Frees everything allocated from iucv_init .
*/
static int iucv_retrieve_buffer ( void ) ;
static void
iucv_exit ( void )
{
iucv_retrieve_buffer ( ) ;
2005-11-07 12:01:30 +03:00
kfree ( iucv_external_int_buffer ) ;
iucv_external_int_buffer = NULL ;
kfree ( iucv_param_pool ) ;
iucv_param_pool = NULL ;
2005-04-17 02:20:36 +04:00
s390_root_dev_unregister ( iucv_root ) ;
bus_unregister ( & iucv_bus ) ;
printk ( KERN_INFO " IUCV lowlevel driver unloaded \n " ) ;
}
/**
* grab_param : - Get a parameter buffer from the pre - allocated pool .
*
* This function searches for an unused element in the pre - allocated pool
* of parameter buffers . If one is found , it marks it " in use " and returns
* a pointer to it . The calling function is responsible for releasing it
* when it has finished its usage .
*
* Returns : A pointer to iucv_param .
*/
static __inline__ iucv_param *
grab_param ( void )
{
iucv_param * ptr ;
static int hint = 0 ;
ptr = iucv_param_pool + hint ;
do {
ptr + + ;
if ( ptr > = iucv_param_pool + PARAM_POOL_SIZE )
ptr = iucv_param_pool ;
2006-01-06 11:19:07 +03:00
} while ( atomic_cmpxchg ( & ptr - > in_use , 0 , 1 ) ! = 0 ) ;
2005-04-17 02:20:36 +04:00
hint = ptr - iucv_param_pool ;
memset ( & ptr - > param , 0 , sizeof ( ptr - > param ) ) ;
return ptr ;
}
/**
* release_param - Release a parameter buffer .
* @ p : A pointer to a struct iucv_param , previously obtained by calling
* grab_param ( ) .
*
* This function marks the specified parameter buffer " unused " .
*/
static __inline__ void
release_param ( void * p )
{
atomic_set ( & ( ( iucv_param * ) p ) - > in_use , 0 ) ;
}
/**
* iucv_add_handler : - Add a new handler
* @ new_handler : handle that is being entered into chain .
*
* Places new handle on iucv_handler_table , if identical handler is not
* found .
*
* Returns : 0 on success , ! 0 on failure ( handler already in chain ) .
*/
static int
iucv_add_handler ( handler * new )
{
ulong flags ;
iucv_debug ( 1 , " entering " ) ;
iucv_dumpit ( " handler: " , new , sizeof ( handler ) ) ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
if ( ! list_empty ( & iucv_handler_table ) ) {
struct list_head * lh ;
/**
* Search list for handler with identical id . If one
* is found , the new handler is _not_ added .
*/
list_for_each ( lh , & iucv_handler_table ) {
handler * h = list_entry ( lh , handler , list ) ;
if ( ! memcmp ( & new - > id , & h - > id , sizeof ( h - > id ) ) ) {
iucv_debug ( 1 , " ret 1 " ) ;
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
return 1 ;
}
}
}
/**
* If we get here , no handler was found .
*/
INIT_LIST_HEAD ( & new - > list ) ;
list_add ( & new - > list , & iucv_handler_table ) ;
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
iucv_debug ( 1 , " exiting " ) ;
return 0 ;
}
/**
* b2f0 :
* @ code : identifier of IUCV call to CP .
* @ parm : pointer to 40 byte iparml area passed to CP
*
* Calls CP to execute IUCV commands .
*
* Returns : return code from CP ' s IUCV call
*/
static __inline__ ulong
b2f0 ( __u32 code , void * parm )
{
iucv_dumpit ( " iparml before b2f0 call: " , parm , sizeof ( iucv_param ) ) ;
asm volatile (
" LRA 1,0(%1) \n \t "
" LR 0,%0 \n \t "
" .long 0xb2f01000 "
:
: " d " ( code ) , " a " ( parm )
: " 0 " , " 1 "
) ;
iucv_dumpit ( " iparml after b2f0 call: " , parm , sizeof ( iucv_param ) ) ;
return ( unsigned long ) * ( ( __u8 * ) ( parm + 3 ) ) ;
}
/*
* Name : iucv_add_pathid
* Purpose : Adds a path id to the system .
* Input : pathid - pathid that is going to be entered into system
* handle - address of handler that the pathid will be associated
* with .
* pgm_data - token passed in by application .
* Output : 0 : successful addition of pathid
* - EINVAL - pathid entry is being used by another application
* - ENOMEM - storage allocation for a new pathid table failed
*/
static int
__iucv_add_pathid ( __u16 pathid , handler * handler )
{
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " handler is pointing to %p " , handler ) ;
if ( pathid > ( max_connections - 1 ) )
return - EINVAL ;
if ( iucv_pathid_table [ pathid ] ) {
iucv_debug ( 1 , " pathid entry is %p " , iucv_pathid_table [ pathid ] ) ;
printk ( KERN_WARNING
" %s: Pathid being used, error. \n " , __FUNCTION__ ) ;
return - EINVAL ;
}
iucv_pathid_table [ pathid ] = handler ;
iucv_debug ( 1 , " exiting " ) ;
return 0 ;
} /* end of add_pathid function */
static int
iucv_add_pathid ( __u16 pathid , handler * handler )
{
ulong flags ;
int rc ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
rc = __iucv_add_pathid ( pathid , handler ) ;
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
return rc ;
}
static void
iucv_remove_pathid ( __u16 pathid )
{
ulong flags ;
if ( pathid > ( max_connections - 1 ) )
return ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
iucv_pathid_table [ pathid ] = NULL ;
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
}
/**
* iucv_declare_buffer_cpuid
* Register at VM for subsequent IUCV operations . This is executed
* on the reserved CPU iucv_cpuid . Called from iucv_declare_buffer ( ) .
*/
static void
iucv_declare_buffer_cpuid ( void * result )
{
iparml_db * parm ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ipbfadr1 = virt_to_phys ( iucv_external_int_buffer ) ;
if ( ( * ( ( ulong * ) result ) = b2f0 ( DECLARE_BUFFER , parm ) ) = = 1 )
* ( ( ulong * ) result ) = parm - > iprcode ;
release_param ( parm ) ;
}
/**
* iucv_retrieve_buffer_cpuid :
* Unregister IUCV usage at VM . This is always executed on the same
* cpu that registered the buffer to VM .
* Called from iucv_retrieve_buffer ( ) .
*/
static void
iucv_retrieve_buffer_cpuid ( void * cpu )
{
iparml_control * parm ;
parm = ( iparml_control * ) grab_param ( ) ;
b2f0 ( RETRIEVE_BUFFER , parm ) ;
release_param ( parm ) ;
}
/**
* Name : iucv_declare_buffer
* Purpose : Specifies the guests real address of an external
* interrupt .
* Input : void
* Output : iprcode - return code from b2f0 call
*/
static int
iucv_declare_buffer ( void )
{
unsigned long flags ;
ulong b2f0_result ;
iucv_debug ( 1 , " entering " ) ;
b2f0_result = - ENODEV ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
if ( iucv_cpuid = = - 1 ) {
/* Reserve any cpu for use by iucv. */
iucv_cpuid = smp_get_cpu ( CPU_MASK_ALL ) ;
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
smp_call_function_on ( iucv_declare_buffer_cpuid ,
& b2f0_result , 0 , 1 , iucv_cpuid ) ;
if ( b2f0_result ) {
smp_put_cpu ( iucv_cpuid ) ;
iucv_cpuid = - 1 ;
}
iucv_debug ( 1 , " Address of EIB = %p " , iucv_external_int_buffer ) ;
} else {
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
b2f0_result = 0 ;
}
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_retrieve_buffer :
*
* Terminates all use of IUCV .
* Returns : return code from CP
*/
static int
iucv_retrieve_buffer ( void )
{
iucv_debug ( 1 , " entering " ) ;
if ( iucv_cpuid ! = - 1 ) {
smp_call_function_on ( iucv_retrieve_buffer_cpuid ,
2006-07-12 18:41:55 +04:00
NULL , 0 , 1 , iucv_cpuid ) ;
2005-04-17 02:20:36 +04:00
/* Release the cpu reserved by iucv_declare_buffer. */
smp_put_cpu ( iucv_cpuid ) ;
iucv_cpuid = - 1 ;
}
iucv_debug ( 1 , " exiting " ) ;
return 0 ;
}
/**
* iucv_remove_handler :
* @ users_handler : handler to be removed
*
* Remove handler when application unregisters .
*/
static void
iucv_remove_handler ( handler * handler )
{
unsigned long flags ;
if ( ( ! iucv_pathid_table ) | | ( ! handler ) )
return ;
iucv_debug ( 1 , " entering " ) ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
list_del ( & handler - > list ) ;
if ( list_empty ( & iucv_handler_table ) ) {
if ( register_flag ) {
unregister_external_interrupt ( 0x4000 , iucv_irq_handler ) ;
register_flag = 0 ;
}
}
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
iucv_debug ( 1 , " exiting " ) ;
return ;
}
/**
* iucv_register_program :
* @ pgmname : user identification
* @ userid : machine identification
* @ pgmmask : Indicates which bits in the pgmname and userid combined will be
* used to determine who is given control .
* @ ops : Address of interrupt handler table .
* @ pgm_data : Application data to be passed to interrupt handlers .
*
* Registers an application with IUCV .
* Returns :
* The address of handler , or NULL on failure .
* NOTE on pgmmask :
* If pgmname , userid and pgmmask are provided , pgmmask is entered into the
* handler as is .
* If pgmmask is NULL , the internal mask is set to all 0xff ' s
* When userid is NULL , the first 8 bytes of the internal mask are forced
* to 0x00 .
* If pgmmask and userid are NULL , the first 8 bytes of the internal mask
* are forced to 0x00 and the last 16 bytes to 0xff .
*/
iucv_handle_t
iucv_register_program ( __u8 pgmname [ 16 ] ,
__u8 userid [ 8 ] ,
__u8 pgmmask [ 24 ] ,
iucv_interrupt_ops_t * ops , void * pgm_data )
{
ulong rc = 0 ; /* return code from function calls */
handler * new_handler ;
iucv_debug ( 1 , " entering " ) ;
if ( ops = = NULL ) {
/* interrupt table is not defined */
printk ( KERN_WARNING " %s: Interrupt table is not defined, "
" exiting \n " , __FUNCTION__ ) ;
return NULL ;
}
if ( ! pgmname ) {
printk ( KERN_WARNING " %s: pgmname not provided \n " , __FUNCTION__ ) ;
return NULL ;
}
/* Allocate handler entry */
new_handler = ( handler * ) kmalloc ( sizeof ( handler ) , GFP_ATOMIC ) ;
if ( new_handler = = NULL ) {
printk ( KERN_WARNING " %s: storage allocation for new handler "
" failed. \n " , __FUNCTION__ ) ;
return NULL ;
}
if ( ! iucv_pathid_table ) {
if ( iucv_init ( ) ) {
kfree ( new_handler ) ;
return NULL ;
}
max_connections = iucv_query_maxconn ( ) ;
2006-03-24 14:15:31 +03:00
iucv_pathid_table = kcalloc ( max_connections , sizeof ( handler * ) ,
GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( iucv_pathid_table = = NULL ) {
printk ( KERN_WARNING " %s: iucv_pathid_table storage "
" allocation failed \n " , __FUNCTION__ ) ;
kfree ( new_handler ) ;
return NULL ;
}
}
memset ( new_handler , 0 , sizeof ( handler ) ) ;
memcpy ( new_handler - > id . user_data , pgmname ,
sizeof ( new_handler - > id . user_data ) ) ;
if ( userid ) {
memcpy ( new_handler - > id . userid , userid ,
sizeof ( new_handler - > id . userid ) ) ;
ASCEBC ( new_handler - > id . userid ,
sizeof ( new_handler - > id . userid ) ) ;
EBC_TOUPPER ( new_handler - > id . userid ,
sizeof ( new_handler - > id . userid ) ) ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
if ( pgmmask ) {
memcpy ( new_handler - > id . mask , pgmmask ,
sizeof ( new_handler - > id . mask ) ) ;
} else {
memset ( new_handler - > id . mask , 0xFF ,
sizeof ( new_handler - > id . mask ) ) ;
}
} else {
if ( pgmmask ) {
memcpy ( new_handler - > id . mask , pgmmask ,
sizeof ( new_handler - > id . mask ) ) ;
} else {
memset ( new_handler - > id . mask , 0xFF ,
sizeof ( new_handler - > id . mask ) ) ;
}
memset ( new_handler - > id . userid , 0x00 ,
sizeof ( new_handler - > id . userid ) ) ;
}
/* fill in the rest of handler */
new_handler - > pgm_data = pgm_data ;
new_handler - > interrupt_table = ops ;
/*
* Check if someone else is registered with same pgmname , userid
* and mask . If someone is already registered with same pgmname ,
* userid and mask , registration will fail and NULL will be returned
* to the application .
* If identical handler not found , then handler is added to list .
*/
rc = iucv_add_handler ( new_handler ) ;
if ( rc ) {
printk ( KERN_WARNING " %s: Someone already registered with same "
" pgmname, userid, pgmmask \n " , __FUNCTION__ ) ;
kfree ( new_handler ) ;
return NULL ;
}
rc = iucv_declare_buffer ( ) ;
if ( rc ) {
char * err = " Unknown " ;
iucv_remove_handler ( new_handler ) ;
kfree ( new_handler ) ;
switch ( rc ) {
case 0x03 :
err = " Directory error " ;
break ;
case 0x0a :
err = " Invalid length " ;
break ;
case 0x13 :
err = " Buffer already exists " ;
break ;
case 0x3e :
err = " Buffer overlap " ;
break ;
case 0x5c :
err = " Paging or storage error " ;
break ;
}
printk ( KERN_WARNING " %s: iucv_declare_buffer "
" returned error 0x%02lx (%s) \n " , __FUNCTION__ , rc , err ) ;
return NULL ;
}
if ( ! register_flag ) {
/* request the 0x4000 external interrupt */
rc = register_external_interrupt ( 0x4000 , iucv_irq_handler ) ;
if ( rc ) {
iucv_remove_handler ( new_handler ) ;
kfree ( new_handler ) ;
printk ( KERN_WARNING " %s: "
" register_external_interrupt returned %ld \n " ,
__FUNCTION__ , rc ) ;
return NULL ;
}
register_flag = 1 ;
}
iucv_debug ( 1 , " exiting " ) ;
return new_handler ;
} /* end of register function */
/**
* iucv_unregister_program :
* @ handle : address of handler
*
* Unregister application with IUCV .
* Returns :
* 0 on success , - EINVAL , if specified handle is invalid .
*/
int
iucv_unregister_program ( iucv_handle_t handle )
{
handler * h = NULL ;
struct list_head * lh ;
int i ;
ulong flags ;
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " address of handler is %p " , h ) ;
/* Checking if handle is valid */
spin_lock_irqsave ( & iucv_lock , flags ) ;
list_for_each ( lh , & iucv_handler_table ) {
if ( ( handler * ) handle = = list_entry ( lh , handler , list ) ) {
h = ( handler * ) handle ;
break ;
}
}
if ( ! h ) {
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
if ( handle )
printk ( KERN_WARNING
" %s: Handler not found in iucv_handler_table. \n " ,
__FUNCTION__ ) ;
else
printk ( KERN_WARNING
" %s: NULL handle passed by application. \n " ,
__FUNCTION__ ) ;
return - EINVAL ;
}
/**
* First , walk thru iucv_pathid_table and sever any pathid which is
* still pointing to the handler to be removed .
*/
for ( i = 0 ; i < max_connections ; i + + )
if ( iucv_pathid_table [ i ] = = h ) {
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
iucv_sever ( i , h - > id . user_data ) ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
}
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
iucv_remove_handler ( h ) ;
kfree ( h ) ;
iucv_debug ( 1 , " exiting " ) ;
return 0 ;
}
/**
* iucv_accept :
* @ pathid : Path identification number
* @ msglim_reqstd : The number of outstanding messages requested .
* @ user_data : Data specified by the iucv_connect function .
* @ flags1 : Contains options for this path .
* - IPPRTY ( 0x20 ) Specifies if you want to send priority message .
* - IPRMDATA ( 0x80 ) Specifies whether your program can handle a message
* in the parameter list .
* - IPQUSCE ( 0x40 ) Specifies whether you want to quiesce the path being
* established .
* @ handle : Address of handler .
* @ pgm_data : Application data passed to interrupt handlers .
* @ flags1_out : Pointer to an int . If not NULL , on return the options for
* the path are stored at the given location :
* - IPPRTY ( 0x20 ) Indicates you may send a priority message .
* @ msglim : Pointer to an __u16 . If not NULL , on return the maximum
* number of outstanding messages is stored at the given
* location .
*
* This function is issued after the user receives a Connection Pending external
* interrupt and now wishes to complete the IUCV communication path .
* Returns :
* return code from CP
*/
int
iucv_accept ( __u16 pathid , __u16 msglim_reqstd ,
__u8 user_data [ 16 ] , int flags1 ,
iucv_handle_t handle , void * pgm_data ,
int * flags1_out , __u16 * msglim )
{
ulong b2f0_result = 0 ;
ulong flags ;
struct list_head * lh ;
handler * h = NULL ;
iparml_control * parm ;
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " pathid = %d " , pathid ) ;
/* Checking if handle is valid */
spin_lock_irqsave ( & iucv_lock , flags ) ;
list_for_each ( lh , & iucv_handler_table ) {
if ( ( handler * ) handle = = list_entry ( lh , handler , list ) ) {
h = ( handler * ) handle ;
break ;
}
}
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
if ( ! h ) {
if ( handle )
printk ( KERN_WARNING
" %s: Handler not found in iucv_handler_table. \n " ,
__FUNCTION__ ) ;
else
printk ( KERN_WARNING
" %s: NULL handle passed by application. \n " ,
__FUNCTION__ ) ;
return - EINVAL ;
}
parm = ( iparml_control * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > ipmsglim = msglim_reqstd ;
if ( user_data )
memcpy ( parm - > ipuser , user_data , sizeof ( parm - > ipuser ) ) ;
parm - > ipflags1 = ( __u8 ) flags1 ;
b2f0_result = b2f0 ( ACCEPT , parm ) ;
if ( ! b2f0_result ) {
if ( msglim )
* msglim = parm - > ipmsglim ;
if ( pgm_data )
h - > pgm_data = pgm_data ;
if ( flags1_out )
* flags1_out = ( parm - > ipflags1 & IPPRTY ) ? IPPRTY : 0 ;
}
release_param ( parm ) ;
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_connect :
* @ pathid : Path identification number
* @ msglim_reqstd : Number of outstanding messages requested
* @ user_data : 16 - byte user data
* @ userid : 8 - byte of user identification
* @ system_name : 8 - byte identifying the system name
* @ flags1 : Specifies options for this path :
* - IPPRTY ( 0x20 ) Specifies if you want to send priority message .
* - IPRMDATA ( 0x80 ) Specifies whether your program can handle a message
* in the parameter list .
* - IPQUSCE ( 0x40 ) Specifies whether you want to quiesce the path being
* established .
* - IPLOCAL ( 0x01 ) Allows an application to force the partner to be on the
* local system . If local is specified then target class
* cannot be specified .
* @ flags1_out : Pointer to an int . If not NULL , on return the options for
* the path are stored at the given location :
* - IPPRTY ( 0x20 ) Indicates you may send a priority message .
* @ msglim : Pointer to an __u16 . If not NULL , on return the maximum
* number of outstanding messages is stored at the given
* location .
* @ handle : Address of handler .
* @ pgm_data : Application data to be passed to interrupt handlers .
*
* This function establishes an IUCV path . Although the connect may complete
* successfully , you are not able to use the path until you receive an IUCV
* Connection Complete external interrupt .
* Returns : return code from CP , or one of the following
* - ENOMEM
* - return code from iucv_declare_buffer
* - EINVAL - invalid handle passed by application
* - EINVAL - pathid address is NULL
* - ENOMEM - pathid table storage allocation failed
* - return code from internal function add_pathid
*/
int
iucv_connect ( __u16 * pathid , __u16 msglim_reqstd ,
__u8 user_data [ 16 ] , __u8 userid [ 8 ] ,
__u8 system_name [ 8 ] , int flags1 ,
int * flags1_out , __u16 * msglim ,
iucv_handle_t handle , void * pgm_data )
{
iparml_control * parm ;
iparml_control local_parm ;
struct list_head * lh ;
ulong b2f0_result = 0 ;
ulong flags ;
int add_pathid_result = 0 ;
handler * h = NULL ;
__u8 no_memory [ 16 ] = " NO MEMORY " ;
iucv_debug ( 1 , " entering " ) ;
/* Checking if handle is valid */
spin_lock_irqsave ( & iucv_lock , flags ) ;
list_for_each ( lh , & iucv_handler_table ) {
if ( ( handler * ) handle = = list_entry ( lh , handler , list ) ) {
h = ( handler * ) handle ;
break ;
}
}
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
if ( ! h ) {
if ( handle )
printk ( KERN_WARNING
" %s: Handler not found in iucv_handler_table. \n " ,
__FUNCTION__ ) ;
else
printk ( KERN_WARNING
" %s: NULL handle passed by application. \n " ,
__FUNCTION__ ) ;
return - EINVAL ;
}
if ( pathid = = NULL ) {
printk ( KERN_WARNING " %s: NULL pathid pointer \n " ,
__FUNCTION__ ) ;
return - EINVAL ;
}
parm = ( iparml_control * ) grab_param ( ) ;
parm - > ipmsglim = msglim_reqstd ;
if ( user_data )
memcpy ( parm - > ipuser , user_data , sizeof ( parm - > ipuser ) ) ;
if ( userid ) {
memcpy ( parm - > ipvmid , userid , sizeof ( parm - > ipvmid ) ) ;
ASCEBC ( parm - > ipvmid , sizeof ( parm - > ipvmid ) ) ;
EBC_TOUPPER ( parm - > ipvmid , sizeof ( parm - > ipvmid ) ) ;
}
if ( system_name ) {
memcpy ( parm - > iptarget , system_name , sizeof ( parm - > iptarget ) ) ;
ASCEBC ( parm - > iptarget , sizeof ( parm - > iptarget ) ) ;
EBC_TOUPPER ( parm - > iptarget , sizeof ( parm - > iptarget ) ) ;
}
/* In order to establish an IUCV connection, the procedure is:
*
* b2f0 ( CONNECT )
* take the ippathid from the b2f0 call
* register the handler to the ippathid
*
* Unfortunately , the ConnectionEstablished message gets sent after the
* b2f0 ( CONNECT ) call but before the register is handled .
*
* In order for this race condition to be eliminated , the IUCV Control
* Interrupts must be disabled for the above procedure .
*
* David Kennedy < dkennedy @ linuxcare . com >
*/
/* Enable everything but IUCV Control messages */
iucv_setmask ( ~ ( AllInterrupts ) ) ;
messagesDisabled = 1 ;
spin_lock_irqsave ( & iucv_lock , flags ) ;
parm - > ipflags1 = ( __u8 ) flags1 ;
b2f0_result = b2f0 ( CONNECT , parm ) ;
memcpy ( & local_parm , parm , sizeof ( local_parm ) ) ;
release_param ( parm ) ;
parm = & local_parm ;
if ( ! b2f0_result )
add_pathid_result = __iucv_add_pathid ( parm - > ippathid , h ) ;
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
if ( b2f0_result ) {
iucv_setmask ( ~ 0 ) ;
messagesDisabled = 0 ;
return b2f0_result ;
}
* pathid = parm - > ippathid ;
/* Enable everything again */
iucv_setmask ( IUCVControlInterruptsFlag ) ;
if ( msglim )
* msglim = parm - > ipmsglim ;
if ( flags1_out )
* flags1_out = ( parm - > ipflags1 & IPPRTY ) ? IPPRTY : 0 ;
if ( add_pathid_result ) {
iucv_sever ( * pathid , no_memory ) ;
printk ( KERN_WARNING " %s: add_pathid failed with rc = "
" %d \n " , __FUNCTION__ , add_pathid_result ) ;
return ( add_pathid_result ) ;
}
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_purge :
* @ pathid : Path identification number
* @ msgid : Message ID of message to purge .
* @ srccls : Message class of the message to purge .
* @ audit : Pointer to an __u32 . If not NULL , on return , information about
* asynchronous errors that may have affected the normal completion
* of this message ist stored at the given location .
*
* Cancels a message you have sent .
* Returns : return code from CP
*/
int
iucv_purge ( __u16 pathid , __u32 msgid , __u32 srccls , __u32 * audit )
{
iparml_purge * parm ;
ulong b2f0_result = 0 ;
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " pathid = %d " , pathid ) ;
parm = ( iparml_purge * ) grab_param ( ) ;
parm - > ipmsgid = msgid ;
parm - > ippathid = pathid ;
parm - > ipsrccls = srccls ;
parm - > ipflags1 | = ( IPSRCCLS | IPFGMID | IPFGPID ) ;
b2f0_result = b2f0 ( PURGE , parm ) ;
if ( ! b2f0_result & & audit ) {
memcpy ( audit , parm - > ipaudit , sizeof ( parm - > ipaudit ) ) ;
/* parm->ipaudit has only 3 bytes */
* audit > > = 8 ;
}
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
release_param ( parm ) ;
iucv_debug ( 1 , " b2f0_result = %ld " , b2f0_result ) ;
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_query_generic :
* @ want_maxconn : Flag , describing which value is to be returned .
*
* Helper function for iucv_query_maxconn ( ) and iucv_query_bufsize ( ) .
*
* Returns : The buffersize , if want_maxconn is 0 ; the maximum number of
* connections , if want_maxconn is 1 or an error - code < 0 on failure .
*/
static int
iucv_query_generic ( int want_maxconn )
{
iparml_purge * parm = ( iparml_purge * ) grab_param ( ) ;
int bufsize , maxconn ;
int ccode ;
/**
* Call b2f0 and store R0 ( max buffer size ) ,
* R1 ( max connections ) and CC .
*/
asm volatile (
" LRA 1,0(%4) \n \t "
" LR 0,%3 \n \t "
" .long 0xb2f01000 \n \t "
" IPM %0 \n \t "
" SRL %0,28 \n \t "
" ST 0,%1 \n \t "
" ST 1,%2 \n \t "
: " =d " ( ccode ) , " =m " ( bufsize ) , " =m " ( maxconn )
: " d " ( QUERY ) , " a " ( parm )
: " 0 " , " 1 " , " cc "
) ;
release_param ( parm ) ;
if ( ccode )
return - EPERM ;
if ( want_maxconn )
return maxconn ;
return bufsize ;
}
/**
* iucv_query_maxconn :
*
* Determines the maximum number of connections thay may be established .
*
* Returns : Maximum number of connections that can be .
*/
ulong
iucv_query_maxconn ( void )
{
return iucv_query_generic ( 1 ) ;
}
/**
* iucv_query_bufsize :
*
* Determines the size of the external interrupt buffer .
*
* Returns : Size of external interrupt buffer .
*/
ulong
iucv_query_bufsize ( void )
{
return iucv_query_generic ( 0 ) ;
}
/**
* iucv_quiesce :
* @ pathid : Path identification number
* @ user_data : 16 - byte user data
*
* Temporarily suspends incoming messages on an IUCV path .
* You can later reactivate the path by invoking the iucv_resume function .
* Returns : return code from CP
*/
int
iucv_quiesce ( __u16 pathid , __u8 user_data [ 16 ] )
{
iparml_control * parm ;
ulong b2f0_result = 0 ;
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " pathid = %d " , pathid ) ;
parm = ( iparml_control * ) grab_param ( ) ;
memcpy ( parm - > ipuser , user_data , sizeof ( parm - > ipuser ) ) ;
parm - > ippathid = pathid ;
b2f0_result = b2f0 ( QUIESCE , parm ) ;
release_param ( parm ) ;
iucv_debug ( 1 , " b2f0_result = %ld " , b2f0_result ) ;
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_receive :
* @ pathid : Path identification number .
* @ buffer : Address of buffer to receive . Must be below 2 G .
* @ buflen : Length of buffer to receive .
* @ msgid : Specifies the message ID .
* @ trgcls : Specifies target class .
* @ flags1_out : Receives options for path on return .
* - IPNORPY ( 0x10 ) Specifies whether a reply is required
* - IPPRTY ( 0x20 ) Specifies if you want to send priority message
* - IPRMDATA ( 0x80 ) Specifies the data is contained in the parameter list
* @ residual_buffer : Receives the address of buffer updated by the number
* of bytes you have received on return .
* @ residual_length : On return , receives one of the following values :
* - 0 If the receive buffer is the same length as
* the message .
* - Remaining bytes in buffer If the receive buffer is longer than the
* message .
* - Remaining bytes in message If the receive buffer is shorter than the
* message .
*
* This function receives messages that are being sent to you over established
* paths .
* Returns : return code from CP IUCV call ; If the receive buffer is shorter
* than the message , always 5
* - EINVAL - buffer address is pointing to NULL
*/
int
iucv_receive ( __u16 pathid , __u32 msgid , __u32 trgcls ,
void * buffer , ulong buflen ,
int * flags1_out , ulong * residual_buffer , ulong * residual_length )
{
iparml_db * parm ;
ulong b2f0_result ;
int moved = 0 ; /* number of bytes moved from parmlist to buffer */
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ipbfadr1 = ( __u32 ) ( addr_t ) buffer ;
parm - > ipbfln1f = ( __u32 ) ( ( ulong ) buflen ) ;
parm - > ipmsgid = msgid ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipflags1 = ( IPFGPID | IPFGMID | IPFGMCL ) ;
b2f0_result = b2f0 ( RECEIVE , parm ) ;
if ( ! b2f0_result | | b2f0_result = = 5 ) {
if ( flags1_out ) {
iucv_debug ( 2 , " *flags1_out = %d " , * flags1_out ) ;
* flags1_out = ( parm - > ipflags1 & ( ~ 0x07 ) ) ;
iucv_debug ( 2 , " *flags1_out = %d " , * flags1_out ) ;
}
if ( ! ( parm - > ipflags1 & IPRMDATA ) ) { /*msg not in parmlist */
if ( residual_length )
* residual_length = parm - > ipbfln1f ;
if ( residual_buffer )
* residual_buffer = parm - > ipbfadr1 ;
} else {
moved = min_t ( unsigned long , buflen , 8 ) ;
memcpy ( ( char * ) buffer ,
( char * ) & parm - > ipbfadr1 , moved ) ;
if ( buflen < 8 )
b2f0_result = 5 ;
if ( residual_length )
* residual_length = abs ( buflen - 8 ) ;
if ( residual_buffer )
* residual_buffer = ( ulong ) ( buffer + moved ) ;
}
}
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_receive_array
* Purpose : This function receives messages that are being sent to you
* over established paths .
* Input : pathid - path identification number
* buffer - address of array of buffers
* buflen - total length of buffers
* msgid - specifies the message ID .
* trgcls - specifies target class
* Output :
* flags1_out : Options for path .
* IPNORPY - 0x10 specifies whether a reply is required
* IPPRTY - 0x20 specifies if you want to send priority message
* IPRMDATA - 0x80 specifies the data is contained in the parameter list
* residual_buffer - address points to the current list entry IUCV
* is working on .
* residual_length -
* Contains one of the following values , if the receive buffer is :
* The same length as the message , this field is zero .
* Longer than the message , this field contains the number of
* bytes remaining in the buffer .
* Shorter than the message , this field contains the residual
* count ( that is , the number of bytes remaining in the
* message that does not fit into the buffer . In this case
* b2f0_result = 5.
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer address is NULL
*/
int
iucv_receive_array ( __u16 pathid ,
__u32 msgid , __u32 trgcls ,
iucv_array_t * buffer , ulong buflen ,
int * flags1_out ,
ulong * residual_buffer , ulong * residual_length )
{
iparml_db * parm ;
ulong b2f0_result ;
int i = 0 , moved = 0 , need_to_move = 8 , dyn_len ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ipbfadr1 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ipbfln1f = ( __u32 ) buflen ;
parm - > ipmsgid = msgid ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipflags1 = ( IPBUFLST | IPFGPID | IPFGMID | IPFGMCL ) ;
b2f0_result = b2f0 ( RECEIVE , parm ) ;
if ( ! b2f0_result | | b2f0_result = = 5 ) {
if ( flags1_out ) {
iucv_debug ( 2 , " *flags1_out = %d " , * flags1_out ) ;
* flags1_out = ( parm - > ipflags1 & ( ~ 0x07 ) ) ;
iucv_debug ( 2 , " *flags1_out = %d " , * flags1_out ) ;
}
if ( ! ( parm - > ipflags1 & IPRMDATA ) ) { /*msg not in parmlist */
if ( residual_length )
* residual_length = parm - > ipbfln1f ;
if ( residual_buffer )
* residual_buffer = parm - > ipbfadr1 ;
} else {
/* copy msg from parmlist to users array. */
while ( ( moved < 8 ) & & ( moved < buflen ) ) {
dyn_len =
min_t ( unsigned int ,
( buffer + i ) - > length , need_to_move ) ;
memcpy ( ( char * ) ( ( ulong ) ( ( buffer + i ) - > address ) ) ,
( ( char * ) & parm - > ipbfadr1 ) + moved ,
dyn_len ) ;
moved + = dyn_len ;
need_to_move - = dyn_len ;
( buffer + i ) - > address =
( __u32 )
( ( ulong ) ( __u8 * ) ( ( ulong ) ( buffer + i ) - > address )
+ dyn_len ) ;
( buffer + i ) - > length - = dyn_len ;
i + + ;
}
if ( need_to_move ) /* buflen < 8 bytes */
b2f0_result = 5 ;
if ( residual_length )
* residual_length = abs ( buflen - 8 ) ;
if ( residual_buffer ) {
if ( ! moved )
* residual_buffer = ( ulong ) buffer ;
else
* residual_buffer =
( ulong ) ( buffer + ( i - 1 ) ) ;
}
}
}
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_reject :
* @ pathid : Path identification number .
* @ msgid : Message ID of the message to reject .
* @ trgcls : Target class of the message to reject .
* Returns : return code from CP
*
* Refuses a specified message . Between the time you are notified of a
* message and the time that you complete the message , the message may
* be rejected .
*/
int
iucv_reject ( __u16 pathid , __u32 msgid , __u32 trgcls )
{
iparml_db * parm ;
ulong b2f0_result = 0 ;
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " pathid = %d " , pathid ) ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > ipmsgid = msgid ;
parm - > iptrgcls = trgcls ;
parm - > ipflags1 = ( IPFGMCL | IPFGMID | IPFGPID ) ;
b2f0_result = b2f0 ( REJECT , parm ) ;
release_param ( parm ) ;
iucv_debug ( 1 , " b2f0_result = %ld " , b2f0_result ) ;
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_reply
* Purpose : This function responds to the two - way messages that you
* receive . You must identify completely the message to
* which you wish to reply . ie , pathid , msgid , and trgcls .
* Input : pathid - path identification number
* msgid - specifies the message ID .
* trgcls - specifies target class
* flags1 - option for path
* IPPRTY - 0x20 - specifies if you want to send priority message
* buffer - address of reply buffer
* buflen - length of reply buffer
* Output : ipbfadr2 - Address of buffer updated by the number
* of bytes you have moved .
* ipbfln2f - Contains one of the following values :
* If the answer buffer is the same length as the reply , this field
* contains zero .
* If the answer buffer is longer than the reply , this field contains
* the number of bytes remaining in the buffer .
* If the answer buffer is shorter than the reply , this field contains
* a residual count ( that is , the number of bytes remianing in the
* reply that does not fit into the buffer . In this
* case b2f0_result = 5.
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer address is NULL
*/
int
iucv_reply ( __u16 pathid ,
__u32 msgid , __u32 trgcls ,
int flags1 ,
void * buffer , ulong buflen , ulong * ipbfadr2 , ulong * ipbfln2f )
{
iparml_db * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ipbfadr2 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ipbfln2f = ( __u32 ) buflen ; /* length of message */
parm - > ippathid = pathid ;
parm - > ipmsgid = msgid ;
parm - > iptrgcls = trgcls ;
parm - > ipflags1 = ( __u8 ) flags1 ; /* priority message */
b2f0_result = b2f0 ( REPLY , parm ) ;
if ( ( ! b2f0_result ) | | ( b2f0_result = = 5 ) ) {
if ( ipbfadr2 )
* ipbfadr2 = parm - > ipbfadr2 ;
if ( ipbfln2f )
* ipbfln2f = parm - > ipbfln2f ;
}
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_reply_array
* Purpose : This function responds to the two - way messages that you
* receive . You must identify completely the message to
* which you wish to reply . ie , pathid , msgid , and trgcls .
* The array identifies a list of addresses and lengths of
* discontiguous buffers that contains the reply data .
* Input : pathid - path identification number
* msgid - specifies the message ID .
* trgcls - specifies target class
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* buffer - address of array of reply buffers
* buflen - total length of reply buffers
* Output : ipbfadr2 - Address of buffer which IUCV is currently working on .
* ipbfln2f - Contains one of the following values :
* If the answer buffer is the same length as the reply , this field
* contains zero .
* If the answer buffer is longer than the reply , this field contains
* the number of bytes remaining in the buffer .
* If the answer buffer is shorter than the reply , this field contains
* a residual count ( that is , the number of bytes remianing in the
* reply that does not fit into the buffer . In this
* case b2f0_result = 5.
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer address is NULL
*/
int
iucv_reply_array ( __u16 pathid ,
__u32 msgid , __u32 trgcls ,
int flags1 ,
iucv_array_t * buffer ,
ulong buflen , ulong * ipbfadr2 , ulong * ipbfln2f )
{
iparml_db * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ipbfadr2 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ipbfln2f = buflen ; /* length of message */
parm - > ippathid = pathid ;
parm - > ipmsgid = msgid ;
parm - > iptrgcls = trgcls ;
parm - > ipflags1 = ( IPANSLST | flags1 ) ;
b2f0_result = b2f0 ( REPLY , parm ) ;
if ( ( ! b2f0_result ) | | ( b2f0_result = = 5 ) ) {
if ( ipbfadr2 )
* ipbfadr2 = parm - > ipbfadr2 ;
if ( ipbfln2f )
* ipbfln2f = parm - > ipbfln2f ;
}
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_reply_prmmsg
* Purpose : This function responds to the two - way messages that you
* receive . You must identify completely the message to
* which you wish to reply . ie , pathid , msgid , and trgcls .
* Prmmsg signifies the data is moved into the
* parameter list .
* Input : pathid - path identification number
* msgid - specifies the message ID .
* trgcls - specifies target class
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* prmmsg - 8 - bytes of data to be placed into the parameter
* list .
* Output : NA
* Return : b2f0_result - return code from CP
*/
int
iucv_reply_prmmsg ( __u16 pathid ,
__u32 msgid , __u32 trgcls , int flags1 , __u8 prmmsg [ 8 ] )
{
iparml_dpl * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
parm = ( iparml_dpl * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > ipmsgid = msgid ;
parm - > iptrgcls = trgcls ;
memcpy ( parm - > iprmmsg , prmmsg , sizeof ( parm - > iprmmsg ) ) ;
parm - > ipflags1 = ( IPRMDATA | flags1 ) ;
b2f0_result = b2f0 ( REPLY , parm ) ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/**
* iucv_resume :
* @ pathid : Path identification number
* @ user_data : 16 - byte of user data
*
* This function restores communication over a quiesced path .
* Returns : return code from CP
*/
int
iucv_resume ( __u16 pathid , __u8 user_data [ 16 ] )
{
iparml_control * parm ;
ulong b2f0_result = 0 ;
iucv_debug ( 1 , " entering " ) ;
iucv_debug ( 1 , " pathid = %d " , pathid ) ;
parm = ( iparml_control * ) grab_param ( ) ;
memcpy ( parm - > ipuser , user_data , sizeof ( * user_data ) ) ;
parm - > ippathid = pathid ;
b2f0_result = b2f0 ( RESUME , parm ) ;
release_param ( parm ) ;
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send
* Purpose : sends messages
* Input : pathid - ushort , pathid
* msgid - ulong * , id of message returned to caller
* trgcls - ulong , target message class
* srccls - ulong , source message class
* msgtag - ulong , message tag
* flags1 - Contains options for this path .
* IPPRTY - Ox20 - specifies if you want to send a priority message .
* buffer - pointer to buffer
* buflen - ulong , length of buffer
* Output : b2f0_result - return code from b2f0 call
* msgid - returns message id
*/
int
iucv_send ( __u16 pathid , __u32 * msgid ,
__u32 trgcls , __u32 srccls ,
__u32 msgtag , int flags1 , void * buffer , ulong buflen )
{
iparml_db * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ipbfadr1 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipbfln1f = ( __u32 ) buflen ; /* length of message */
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipflags1 = ( IPNORPY | flags1 ) ; /* one way priority message */
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send_array
* Purpose : This function transmits data to another application .
* The contents of buffer is the address of the array of
* addresses and lengths of discontiguous buffers that hold
* the message text . This is a one - way message and the
* receiver will not reply to the message .
* Input : pathid - path identification number
* trgcls - specifies target class
* srccls - specifies the source message class
* msgtag - specifies a tag to be associated witht the message
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* buffer - address of array of send buffers
* buflen - total length of send buffers
* Output : msgid - specifies the message ID .
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer address is NULL
*/
int
iucv_send_array ( __u16 pathid ,
__u32 * msgid ,
__u32 trgcls ,
__u32 srccls ,
__u32 msgtag , int flags1 , iucv_array_t * buffer , ulong buflen )
{
iparml_db * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipbfadr1 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ipbfln1f = ( __u32 ) buflen ; /* length of message */
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipflags1 = ( IPNORPY | IPBUFLST | flags1 ) ;
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send_prmmsg
* Purpose : This function transmits data to another application .
* Prmmsg specifies that the 8 - bytes of data are to be moved
* into the parameter list . This is a one - way message and the
* receiver will not reply to the message .
* Input : pathid - path identification number
* trgcls - specifies target class
* srccls - specifies the source message class
* msgtag - specifies a tag to be associated with the message
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* prmmsg - 8 - bytes of data to be placed into parameter list
* Output : msgid - specifies the message ID .
* Return : b2f0_result - return code from CP
*/
int
iucv_send_prmmsg ( __u16 pathid ,
__u32 * msgid ,
__u32 trgcls ,
__u32 srccls , __u32 msgtag , int flags1 , __u8 prmmsg [ 8 ] )
{
iparml_dpl * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
parm = ( iparml_dpl * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipflags1 = ( IPRMDATA | IPNORPY | flags1 ) ;
memcpy ( parm - > iprmmsg , prmmsg , sizeof ( parm - > iprmmsg ) ) ;
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send2way
* Purpose : This function transmits data to another application .
* Data to be transmitted is in a buffer . The receiver
* of the send is expected to reply to the message and
* a buffer is provided into which IUCV moves the reply
* to this message .
* Input : pathid - path identification number
* trgcls - specifies target class
* srccls - specifies the source message class
* msgtag - specifies a tag associated with the message
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* buffer - address of send buffer
* buflen - length of send buffer
* ansbuf - address of buffer to reply with
* anslen - length of buffer to reply with
* Output : msgid - specifies the message ID .
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer or ansbuf address is NULL
*/
int
iucv_send2way ( __u16 pathid ,
__u32 * msgid ,
__u32 trgcls ,
__u32 srccls ,
__u32 msgtag ,
int flags1 ,
void * buffer , ulong buflen , void * ansbuf , ulong anslen )
{
iparml_db * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer | | ! ansbuf )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipbfadr1 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ipbfln1f = ( __u32 ) buflen ; /* length of message */
parm - > ipbfadr2 = ( __u32 ) ( ( ulong ) ansbuf ) ;
parm - > ipbfln2f = ( __u32 ) anslen ;
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipflags1 = flags1 ; /* priority message */
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send2way_array
* Purpose : This function transmits data to another application .
* The contents of buffer is the address of the array of
* addresses and lengths of discontiguous buffers that hold
* the message text . The receiver of the send is expected to
* reply to the message and a buffer is provided into which
* IUCV moves the reply to this message .
* Input : pathid - path identification number
* trgcls - specifies target class
* srccls - specifies the source message class
* msgtag - spcifies a tag to be associated with the message
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* buffer - address of array of send buffers
* buflen - total length of send buffers
* ansbuf - address of buffer to reply with
* anslen - length of buffer to reply with
* Output : msgid - specifies the message ID .
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer address is NULL
*/
int
iucv_send2way_array ( __u16 pathid ,
__u32 * msgid ,
__u32 trgcls ,
__u32 srccls ,
__u32 msgtag ,
int flags1 ,
iucv_array_t * buffer ,
ulong buflen , iucv_array_t * ansbuf , ulong anslen )
{
iparml_db * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! buffer | | ! ansbuf )
return - EINVAL ;
parm = ( iparml_db * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipbfadr1 = ( __u32 ) ( ( ulong ) buffer ) ;
parm - > ipbfln1f = ( __u32 ) buflen ; /* length of message */
parm - > ipbfadr2 = ( __u32 ) ( ( ulong ) ansbuf ) ;
parm - > ipbfln2f = ( __u32 ) anslen ;
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipflags1 = ( IPBUFLST | IPANSLST | flags1 ) ;
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send2way_prmmsg
* Purpose : This function transmits data to another application .
* Prmmsg specifies that the 8 - bytes of data are to be moved
* into the parameter list . This is a two - way message and the
* receiver of the message is expected to reply . A buffer
* is provided into which IUCV moves the reply to this
* message .
* Input : pathid - path identification number
* trgcls - specifies target class
* srccls - specifies the source message class
* msgtag - specifies a tag to be associated with the message
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* prmmsg - 8 - bytes of data to be placed in parameter list
* ansbuf - address of buffer to reply with
* anslen - length of buffer to reply with
* Output : msgid - specifies the message ID .
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - buffer address is NULL
*/
int
iucv_send2way_prmmsg ( __u16 pathid ,
__u32 * msgid ,
__u32 trgcls ,
__u32 srccls ,
__u32 msgtag ,
ulong flags1 , __u8 prmmsg [ 8 ] , void * ansbuf , ulong anslen )
{
iparml_dpl * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! ansbuf )
return - EINVAL ;
parm = ( iparml_dpl * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipbfadr2 = ( __u32 ) ( ( ulong ) ansbuf ) ;
parm - > ipbfln2f = ( __u32 ) anslen ;
parm - > ipflags1 = ( IPRMDATA | flags1 ) ; /* message in prmlist */
memcpy ( parm - > iprmmsg , prmmsg , sizeof ( parm - > iprmmsg ) ) ;
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
/*
* Name : iucv_send2way_prmmsg_array
* Purpose : This function transmits data to another application .
* Prmmsg specifies that the 8 - bytes of data are to be moved
* into the parameter list . This is a two - way message and the
* receiver of the message is expected to reply . A buffer
* is provided into which IUCV moves the reply to this
* message . The contents of ansbuf is the address of the
* array of addresses and lengths of discontiguous buffers
* that contain the reply .
* Input : pathid - path identification number
* trgcls - specifies target class
* srccls - specifies the source message class
* msgtag - specifies a tag to be associated with the message
* flags1 - option for path
* IPPRTY - specifies if you want to send priority message
* prmmsg - 8 - bytes of data to be placed into the parameter list
* ansbuf - address of buffer to reply with
* anslen - length of buffer to reply with
* Output : msgid - specifies the message ID .
* Return : b2f0_result - return code from CP
* ( - EINVAL ) - ansbuf address is NULL
*/
int
iucv_send2way_prmmsg_array ( __u16 pathid ,
__u32 * msgid ,
__u32 trgcls ,
__u32 srccls ,
__u32 msgtag ,
int flags1 ,
__u8 prmmsg [ 8 ] ,
iucv_array_t * ansbuf , ulong anslen )
{
iparml_dpl * parm ;
ulong b2f0_result ;
iucv_debug ( 2 , " entering " ) ;
if ( ! ansbuf )
return - EINVAL ;
parm = ( iparml_dpl * ) grab_param ( ) ;
parm - > ippathid = pathid ;
parm - > iptrgcls = trgcls ;
parm - > ipsrccls = srccls ;
parm - > ipmsgtag = msgtag ;
parm - > ipbfadr2 = ( __u32 ) ( ( ulong ) ansbuf ) ;
parm - > ipbfln2f = ( __u32 ) anslen ;
parm - > ipflags1 = ( IPRMDATA | IPANSLST | flags1 ) ;
memcpy ( parm - > iprmmsg , prmmsg , sizeof ( parm - > iprmmsg ) ) ;
b2f0_result = b2f0 ( SEND , parm ) ;
if ( ( ! b2f0_result ) & & ( msgid ) )
* msgid = parm - > ipmsgid ;
release_param ( parm ) ;
iucv_debug ( 2 , " exiting " ) ;
return b2f0_result ;
}
void
iucv_setmask_cpuid ( void * result )
{
iparml_set_mask * parm ;
iucv_debug ( 1 , " entering " ) ;
parm = ( iparml_set_mask * ) grab_param ( ) ;
parm - > ipmask = * ( ( __u8 * ) result ) ;
* ( ( ulong * ) result ) = b2f0 ( SETMASK , parm ) ;
release_param ( parm ) ;
iucv_debug ( 1 , " b2f0_result = %ld " , * ( ( ulong * ) result ) ) ;
iucv_debug ( 1 , " exiting " ) ;
}
/*
* Name : iucv_setmask
* Purpose : This function enables or disables the following IUCV
* external interruptions : Nonpriority and priority message
* interrupts , nonpriority and priority reply interrupts .
* Input : SetMaskFlag - options for interrupts
* 0x80 - Nonpriority_MessagePendingInterruptsFlag
* 0x40 - Priority_MessagePendingInterruptsFlag
* 0x20 - Nonpriority_MessageCompletionInterruptsFlag
* 0x10 - Priority_MessageCompletionInterruptsFlag
* 0x08 - IUCVControlInterruptsFlag
* Output : NA
* Return : b2f0_result - return code from CP
*/
int
iucv_setmask ( int SetMaskFlag )
{
union {
ulong result ;
__u8 param ;
} u ;
int cpu ;
u . param = SetMaskFlag ;
cpu = get_cpu ( ) ;
smp_call_function_on ( iucv_setmask_cpuid , & u , 0 , 1 , iucv_cpuid ) ;
put_cpu ( ) ;
return u . result ;
}
/**
* iucv_sever :
* @ pathid : Path identification number
* @ user_data : 16 - byte of user data
*
* This function terminates an iucv path .
* Returns : return code from CP
*/
int
iucv_sever ( __u16 pathid , __u8 user_data [ 16 ] )
{
iparml_control * parm ;
ulong b2f0_result = 0 ;
iucv_debug ( 1 , " entering " ) ;
parm = ( iparml_control * ) grab_param ( ) ;
memcpy ( parm - > ipuser , user_data , sizeof ( parm - > ipuser ) ) ;
parm - > ippathid = pathid ;
b2f0_result = b2f0 ( SEVER , parm ) ;
if ( ! b2f0_result )
iucv_remove_pathid ( pathid ) ;
release_param ( parm ) ;
iucv_debug ( 1 , " exiting " ) ;
return b2f0_result ;
}
/*
* Interrupt Handlers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* iucv_irq_handler :
* @ regs : Current registers
* @ code : irq code
*
* Handles external interrupts coming in from CP .
* Places the interrupt buffer on a queue and schedules iucv_tasklet_handler ( ) .
*/
static void
iucv_irq_handler ( struct pt_regs * regs , __u16 code )
{
iucv_irqdata * irqdata ;
irqdata = kmalloc ( sizeof ( iucv_irqdata ) , GFP_ATOMIC ) ;
if ( ! irqdata ) {
printk ( KERN_WARNING " %s: out of memory \n " , __FUNCTION__ ) ;
return ;
}
memcpy ( & irqdata - > data , iucv_external_int_buffer ,
sizeof ( iucv_GeneralInterrupt ) ) ;
spin_lock ( & iucv_irq_queue_lock ) ;
list_add_tail ( & irqdata - > queue , & iucv_irq_queue ) ;
spin_unlock ( & iucv_irq_queue_lock ) ;
tasklet_schedule ( & iucv_tasklet ) ;
}
/**
* iucv_do_int :
* @ int_buf : Pointer to copy of external interrupt buffer
*
* The workhorse for handling interrupts queued by iucv_irq_handler ( ) .
* This function is called from the bottom half iucv_tasklet_handler ( ) .
*/
static void
iucv_do_int ( iucv_GeneralInterrupt * int_buf )
{
handler * h = NULL ;
struct list_head * lh ;
ulong flags ;
iucv_interrupt_ops_t * interrupt = NULL ; /* interrupt addresses */
__u8 temp_buff1 [ 24 ] , temp_buff2 [ 24 ] ; /* masked handler id. */
int rc = 0 , j = 0 ;
__u8 no_listener [ 16 ] = " NO LISTENER " ;
iucv_debug ( 2 , " entering, pathid %d, type %02X " ,
int_buf - > ippathid , int_buf - > iptype ) ;
iucv_dumpit ( " External Interrupt Buffer: " ,
int_buf , sizeof ( iucv_GeneralInterrupt ) ) ;
ASCEBC ( no_listener , 16 ) ;
if ( int_buf - > iptype ! = 01 ) {
if ( ( int_buf - > ippathid ) > ( max_connections - 1 ) ) {
printk ( KERN_WARNING " %s: Got interrupt with pathid %d "
" > max_connections (%ld) \n " , __FUNCTION__ ,
int_buf - > ippathid , max_connections - 1 ) ;
} else {
h = iucv_pathid_table [ int_buf - > ippathid ] ;
interrupt = h - > interrupt_table ;
iucv_dumpit ( " Handler: " , h , sizeof ( handler ) ) ;
}
}
/* end of if statement */
switch ( int_buf - > iptype ) {
case 0x01 : /* connection pending */
if ( messagesDisabled ) {
iucv_setmask ( ~ 0 ) ;
messagesDisabled = 0 ;
}
spin_lock_irqsave ( & iucv_lock , flags ) ;
list_for_each ( lh , & iucv_handler_table ) {
h = list_entry ( lh , handler , list ) ;
memcpy ( temp_buff1 , & ( int_buf - > ipvmid ) , 24 ) ;
memcpy ( temp_buff2 , & ( h - > id . userid ) , 24 ) ;
for ( j = 0 ; j < 24 ; j + + ) {
temp_buff1 [ j ] & = ( h - > id . mask ) [ j ] ;
temp_buff2 [ j ] & = ( h - > id . mask ) [ j ] ;
}
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
iucv_dumpit ( " temp_buff1: " ,
temp_buff1 , sizeof ( temp_buff1 ) ) ;
iucv_dumpit ( " temp_buff2 " ,
temp_buff2 , sizeof ( temp_buff2 ) ) ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
if ( ! memcmp ( temp_buff1 , temp_buff2 , 24 ) ) {
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
iucv_debug ( 2 ,
" found a matching handler " ) ;
break ;
} else
h = NULL ;
}
spin_unlock_irqrestore ( & iucv_lock , flags ) ;
if ( h ) {
/* ADD PATH TO PATHID TABLE */
rc = iucv_add_pathid ( int_buf - > ippathid , h ) ;
if ( rc ) {
iucv_sever ( int_buf - > ippathid ,
no_listener ) ;
iucv_debug ( 1 ,
" add_pathid failed, rc = %d " ,
rc ) ;
} else {
interrupt = h - > interrupt_table ;
if ( interrupt - > ConnectionPending ) {
EBCASC ( int_buf - > ipvmid , 8 ) ;
interrupt - > ConnectionPending (
( iucv_ConnectionPending * ) int_buf ,
h - > pgm_data ) ;
} else
iucv_sever ( int_buf - > ippathid ,
no_listener ) ;
}
} else
iucv_sever ( int_buf - > ippathid , no_listener ) ;
break ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
case 0x02 : /*connection complete */
if ( messagesDisabled ) {
iucv_setmask ( ~ 0 ) ;
messagesDisabled = 0 ;
}
if ( h ) {
if ( interrupt - > ConnectionComplete )
{
interrupt - > ConnectionComplete (
( iucv_ConnectionComplete * ) int_buf ,
h - > pgm_data ) ;
}
else
iucv_debug ( 1 ,
" ConnectionComplete not called " ) ;
} else
iucv_sever ( int_buf - > ippathid , no_listener ) ;
break ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
case 0x03 : /* connection severed */
if ( messagesDisabled ) {
iucv_setmask ( ~ 0 ) ;
messagesDisabled = 0 ;
}
if ( h ) {
if ( interrupt - > ConnectionSevered )
interrupt - > ConnectionSevered (
( iucv_ConnectionSevered * ) int_buf ,
h - > pgm_data ) ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
else
iucv_sever ( int_buf - > ippathid , no_listener ) ;
} else
iucv_sever ( int_buf - > ippathid , no_listener ) ;
break ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
case 0x04 : /* connection quiesced */
if ( messagesDisabled ) {
iucv_setmask ( ~ 0 ) ;
messagesDisabled = 0 ;
}
if ( h ) {
if ( interrupt - > ConnectionQuiesced )
interrupt - > ConnectionQuiesced (
( iucv_ConnectionQuiesced * ) int_buf ,
h - > pgm_data ) ;
else
iucv_debug ( 1 ,
" ConnectionQuiesced not called " ) ;
}
break ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
case 0x05 : /* connection resumed */
if ( messagesDisabled ) {
iucv_setmask ( ~ 0 ) ;
messagesDisabled = 0 ;
}
if ( h ) {
if ( interrupt - > ConnectionResumed )
interrupt - > ConnectionResumed (
( iucv_ConnectionResumed * ) int_buf ,
h - > pgm_data ) ;
else
iucv_debug ( 1 ,
" ConnectionResumed not called " ) ;
}
break ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
case 0x06 : /* priority message complete */
case 0x07 : /* nonpriority message complete */
if ( h ) {
if ( interrupt - > MessageComplete )
interrupt - > MessageComplete (
( iucv_MessageComplete * ) int_buf ,
h - > pgm_data ) ;
else
iucv_debug ( 2 ,
" MessageComplete not called " ) ;
}
break ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
case 0x08 : /* priority message pending */
case 0x09 : /* nonpriority message pending */
if ( h ) {
if ( interrupt - > MessagePending )
interrupt - > MessagePending (
( iucv_MessagePending * ) int_buf ,
h - > pgm_data ) ;
else
iucv_debug ( 2 ,
" MessagePending not called " ) ;
}
break ;
default : /* unknown iucv type */
printk ( KERN_WARNING " %s: unknown iucv interrupt \n " ,
__FUNCTION__ ) ;
break ;
} /* end switch */
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
iucv_debug ( 2 , " exiting pathid %d, type %02X " ,
int_buf - > ippathid , int_buf - > iptype ) ;
return ;
}
/**
* iucv_tasklet_handler :
*
* This function loops over the queue of irq buffers and runs iucv_do_int ( )
* on every queue element .
*/
static void
iucv_tasklet_handler ( unsigned long ignored )
{
struct list_head head ;
struct list_head * next ;
ulong flags ;
spin_lock_irqsave ( & iucv_irq_queue_lock , flags ) ;
list_add ( & head , & iucv_irq_queue ) ;
list_del_init ( & iucv_irq_queue ) ;
spin_unlock_irqrestore ( & iucv_irq_queue_lock , flags ) ;
next = head . next ;
while ( next ! = & head ) {
iucv_irqdata * p = list_entry ( next , iucv_irqdata , queue ) ;
next = next - > next ;
iucv_do_int ( & p - > data ) ;
kfree ( p ) ;
}
return ;
}
subsys_initcall ( iucv_init ) ;
module_exit ( iucv_exit ) ;
/**
* Export all public stuff
*/
EXPORT_SYMBOL ( iucv_bus ) ;
EXPORT_SYMBOL ( iucv_root ) ;
EXPORT_SYMBOL ( iucv_accept ) ;
EXPORT_SYMBOL ( iucv_connect ) ;
#if 0
EXPORT_SYMBOL ( iucv_purge ) ;
EXPORT_SYMBOL ( iucv_query_maxconn ) ;
EXPORT_SYMBOL ( iucv_query_bufsize ) ;
EXPORT_SYMBOL ( iucv_quiesce ) ;
# endif
EXPORT_SYMBOL ( iucv_receive ) ;
#if 0
EXPORT_SYMBOL ( iucv_receive_array ) ;
# endif
EXPORT_SYMBOL ( iucv_reject ) ;
#if 0
EXPORT_SYMBOL ( iucv_reply ) ;
EXPORT_SYMBOL ( iucv_reply_array ) ;
EXPORT_SYMBOL ( iucv_resume ) ;
# endif
EXPORT_SYMBOL ( iucv_reply_prmmsg ) ;
EXPORT_SYMBOL ( iucv_send ) ;
EXPORT_SYMBOL ( iucv_send2way ) ;
EXPORT_SYMBOL ( iucv_send2way_array ) ;
EXPORT_SYMBOL ( iucv_send2way_prmmsg ) ;
EXPORT_SYMBOL ( iucv_send2way_prmmsg_array ) ;
2005-05-12 22:37:00 +04:00
#if 0
EXPORT_SYMBOL ( iucv_send_array ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( iucv_send_prmmsg ) ;
EXPORT_SYMBOL ( iucv_setmask ) ;
# endif
EXPORT_SYMBOL ( iucv_sever ) ;
EXPORT_SYMBOL ( iucv_register_program ) ;
EXPORT_SYMBOL ( iucv_unregister_program ) ;