2005-04-17 02:20:36 +04:00
/*
* Author : Martin Peschke < mpeschke @ de . ibm . com >
* Copyright ( C ) 2001 IBM Entwicklung GmbH , IBM Corporation
*
* SCLP Control - Program Identification .
*/
# include <linux/version.h>
# include <linux/kmod.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <asm/ebcdic.h>
# include <asm/semaphore.h>
# include "sclp.h"
# include "sclp_rw.h"
# define CPI_LENGTH_SYSTEM_TYPE 8
# define CPI_LENGTH_SYSTEM_NAME 8
# define CPI_LENGTH_SYSPLEX_NAME 8
struct cpi_evbuf {
struct evbuf_header header ;
u8 id_format ;
u8 reserved0 ;
u8 system_type [ CPI_LENGTH_SYSTEM_TYPE ] ;
u64 reserved1 ;
u8 system_name [ CPI_LENGTH_SYSTEM_NAME ] ;
u64 reserved2 ;
u64 system_level ;
u64 reserved3 ;
u8 sysplex_name [ CPI_LENGTH_SYSPLEX_NAME ] ;
u8 reserved4 [ 16 ] ;
} __attribute__ ( ( packed ) ) ;
struct cpi_sccb {
struct sccb_header header ;
struct cpi_evbuf cpi_evbuf ;
} __attribute__ ( ( packed ) ) ;
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_cpi_event =
{
2007-04-27 18:01:53 +04:00
. send_mask = EVTYP_CTLPROGIDENT_MASK
2005-04-17 02:20:36 +04:00
} ;
2006-12-15 19:18:20 +03:00
MODULE_LICENSE ( " GPL " ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR (
" Martin Peschke, IBM Deutschland Entwicklung GmbH "
" <mpeschke@de.ibm.com> " ) ;
MODULE_DESCRIPTION (
" identify this operating system instance to the S/390 "
" or zSeries hardware " ) ;
static char * system_name = NULL ;
module_param ( system_name , charp , 0 ) ;
MODULE_PARM_DESC ( system_name , " e.g. hostname - max. 8 characters " ) ;
static char * sysplex_name = NULL ;
# ifdef ALLOW_SYSPLEX_NAME
module_param ( sysplex_name , charp , 0 ) ;
MODULE_PARM_DESC ( sysplex_name , " if applicable - max. 8 characters " ) ;
# endif
/* use default value for this field (as well as for system level) */
static char * system_type = " LINUX " ;
static int
cpi_check_parms ( void )
{
/* reject if no system type specified */
if ( ! system_type ) {
printk ( " cpi: bug: no system type specified \n " ) ;
return - EINVAL ;
}
/* reject if system type larger than 8 characters */
if ( strlen ( system_type ) > CPI_LENGTH_SYSTEM_NAME ) {
printk ( " cpi: bug: system type has length of %li characters - "
" only %i characters supported \n " ,
strlen ( system_type ) , CPI_LENGTH_SYSTEM_TYPE ) ;
return - EINVAL ;
}
/* reject if no system name specified */
if ( ! system_name ) {
printk ( " cpi: no system name specified \n " ) ;
return - EINVAL ;
}
/* reject if system name larger than 8 characters */
if ( strlen ( system_name ) > CPI_LENGTH_SYSTEM_NAME ) {
printk ( " cpi: system name has length of %li characters - "
" only %i characters supported \n " ,
strlen ( system_name ) , CPI_LENGTH_SYSTEM_NAME ) ;
return - EINVAL ;
}
/* reject if specified sysplex name larger than 8 characters */
if ( sysplex_name & & strlen ( sysplex_name ) > CPI_LENGTH_SYSPLEX_NAME ) {
printk ( " cpi: sysplex name has length of %li characters "
" - only %i characters supported \n " ,
strlen ( sysplex_name ) , CPI_LENGTH_SYSPLEX_NAME ) ;
return - EINVAL ;
}
return 0 ;
}
static void
cpi_callback ( struct sclp_req * req , void * data )
{
struct semaphore * sem ;
sem = ( struct semaphore * ) data ;
up ( sem ) ;
}
static struct sclp_req *
cpi_prepare_req ( void )
{
struct sclp_req * req ;
struct cpi_sccb * sccb ;
struct cpi_evbuf * evb ;
2006-12-13 11:35:56 +03:00
req = kmalloc ( sizeof ( struct sclp_req ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( req = = NULL )
return ERR_PTR ( - ENOMEM ) ;
sccb = ( struct cpi_sccb * ) __get_free_page ( GFP_KERNEL | GFP_DMA ) ;
if ( sccb = = NULL ) {
kfree ( req ) ;
return ERR_PTR ( - ENOMEM ) ;
}
memset ( sccb , 0 , sizeof ( struct cpi_sccb ) ) ;
/* setup SCCB for Control-Program Identification */
sccb - > header . length = sizeof ( struct cpi_sccb ) ;
sccb - > cpi_evbuf . header . length = sizeof ( struct cpi_evbuf ) ;
sccb - > cpi_evbuf . header . type = 0x0B ;
evb = & sccb - > cpi_evbuf ;
/* set system type */
memset ( evb - > system_type , ' ' , CPI_LENGTH_SYSTEM_TYPE ) ;
memcpy ( evb - > system_type , system_type , strlen ( system_type ) ) ;
sclp_ascebc_str ( evb - > system_type , CPI_LENGTH_SYSTEM_TYPE ) ;
EBC_TOUPPER ( evb - > system_type , CPI_LENGTH_SYSTEM_TYPE ) ;
/* set system name */
memset ( evb - > system_name , ' ' , CPI_LENGTH_SYSTEM_NAME ) ;
memcpy ( evb - > system_name , system_name , strlen ( system_name ) ) ;
sclp_ascebc_str ( evb - > system_name , CPI_LENGTH_SYSTEM_NAME ) ;
EBC_TOUPPER ( evb - > system_name , CPI_LENGTH_SYSTEM_NAME ) ;
2007-10-20 01:10:43 +04:00
/* set system level */
2005-04-17 02:20:36 +04:00
evb - > system_level = LINUX_VERSION_CODE ;
/* set sysplex name */
if ( sysplex_name ) {
memset ( evb - > sysplex_name , ' ' , CPI_LENGTH_SYSPLEX_NAME ) ;
memcpy ( evb - > sysplex_name , sysplex_name , strlen ( sysplex_name ) ) ;
sclp_ascebc_str ( evb - > sysplex_name , CPI_LENGTH_SYSPLEX_NAME ) ;
EBC_TOUPPER ( evb - > sysplex_name , CPI_LENGTH_SYSPLEX_NAME ) ;
}
/* prepare request data structure presented to SCLP driver */
2007-02-05 23:18:37 +03:00
req - > command = SCLP_CMDW_WRITE_EVENT_DATA ;
2005-04-17 02:20:36 +04:00
req - > sccb = sccb ;
req - > status = SCLP_REQ_FILLED ;
req - > callback = cpi_callback ;
return req ;
}
static void
cpi_free_req ( struct sclp_req * req )
{
free_page ( ( unsigned long ) req - > sccb ) ;
kfree ( req ) ;
}
static int __init
cpi_module_init ( void )
{
struct semaphore sem ;
struct sclp_req * req ;
int rc ;
rc = cpi_check_parms ( ) ;
if ( rc )
return rc ;
rc = sclp_register ( & sclp_cpi_event ) ;
if ( rc ) {
/* could not register sclp event. Die. */
printk ( KERN_WARNING " cpi: could not register to hardware "
" console. \n " ) ;
return - EINVAL ;
}
2007-04-27 18:01:53 +04:00
if ( ! ( sclp_cpi_event . sclp_send_mask & EVTYP_CTLPROGIDENT_MASK ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " cpi: no control program identification "
" support \n " ) ;
sclp_unregister ( & sclp_cpi_event ) ;
2006-01-06 11:19:27 +03:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
req = cpi_prepare_req ( ) ;
if ( IS_ERR ( req ) ) {
printk ( KERN_WARNING " cpi: couldn't allocate request \n " ) ;
sclp_unregister ( & sclp_cpi_event ) ;
return PTR_ERR ( req ) ;
}
/* Prepare semaphore */
sema_init ( & sem , 0 ) ;
req - > callback_data = & sem ;
/* Add request to sclp queue */
rc = sclp_add_request ( req ) ;
if ( rc ) {
printk ( KERN_WARNING " cpi: could not start request \n " ) ;
cpi_free_req ( req ) ;
sclp_unregister ( & sclp_cpi_event ) ;
return rc ;
}
/* make "insmod" sleep until callback arrives */
down ( & sem ) ;
rc = ( ( struct cpi_sccb * ) req - > sccb ) - > header . response_code ;
if ( rc ! = 0x0020 ) {
printk ( KERN_WARNING " cpi: failed with response code 0x%x \n " ,
rc ) ;
rc = - ECOMM ;
} else
rc = 0 ;
cpi_free_req ( req ) ;
sclp_unregister ( & sclp_cpi_event ) ;
return rc ;
}
static void __exit cpi_module_exit ( void )
{
}
/* declare driver module init/cleanup functions */
module_init ( cpi_module_init ) ;
module_exit ( cpi_module_exit ) ;