|  | /* | 
|  | * 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 = | 
|  | { | 
|  | .send_mask = EvTyp_CtlProgIdent_Mask | 
|  | }; | 
|  |  | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL); | 
|  | 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); | 
|  |  | 
|  | /* set sytem level */ | 
|  | 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 */ | 
|  | req->command = SCLP_CMDW_WRITE_EVENT_DATA; | 
|  | 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; | 
|  | } | 
|  | if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { | 
|  | printk(KERN_WARNING "cpi: no control program identification " | 
|  | "support\n"); | 
|  | sclp_unregister(&sclp_cpi_event); | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  |