| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Author: Martin Peschke <mpeschke@de.ibm.com> | 
|  | 3 | * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation | 
|  | 4 | * | 
|  | 5 | * SCLP Control-Program Identification. | 
|  | 6 | */ | 
|  | 7 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 | #include <linux/version.h> | 
|  | 9 | #include <linux/kmod.h> | 
|  | 10 | #include <linux/module.h> | 
|  | 11 | #include <linux/moduleparam.h> | 
|  | 12 | #include <linux/init.h> | 
|  | 13 | #include <linux/timer.h> | 
|  | 14 | #include <linux/string.h> | 
|  | 15 | #include <linux/err.h> | 
|  | 16 | #include <linux/slab.h> | 
|  | 17 | #include <asm/ebcdic.h> | 
|  | 18 | #include <asm/semaphore.h> | 
|  | 19 |  | 
|  | 20 | #include "sclp.h" | 
|  | 21 | #include "sclp_rw.h" | 
|  | 22 |  | 
|  | 23 | #define CPI_LENGTH_SYSTEM_TYPE	8 | 
|  | 24 | #define CPI_LENGTH_SYSTEM_NAME	8 | 
|  | 25 | #define CPI_LENGTH_SYSPLEX_NAME	8 | 
|  | 26 |  | 
|  | 27 | struct cpi_evbuf { | 
|  | 28 | struct evbuf_header header; | 
|  | 29 | u8	id_format; | 
|  | 30 | u8	reserved0; | 
|  | 31 | u8	system_type[CPI_LENGTH_SYSTEM_TYPE]; | 
|  | 32 | u64	reserved1; | 
|  | 33 | u8	system_name[CPI_LENGTH_SYSTEM_NAME]; | 
|  | 34 | u64	reserved2; | 
|  | 35 | u64	system_level; | 
|  | 36 | u64	reserved3; | 
|  | 37 | u8	sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; | 
|  | 38 | u8	reserved4[16]; | 
|  | 39 | } __attribute__((packed)); | 
|  | 40 |  | 
|  | 41 | struct cpi_sccb { | 
|  | 42 | struct sccb_header header; | 
|  | 43 | struct cpi_evbuf cpi_evbuf; | 
|  | 44 | } __attribute__((packed)); | 
|  | 45 |  | 
|  | 46 | /* Event type structure for write message and write priority message */ | 
|  | 47 | static struct sclp_register sclp_cpi_event = | 
|  | 48 | { | 
|  | 49 | .send_mask = EvTyp_CtlProgIdent_Mask | 
|  | 50 | }; | 
|  | 51 |  | 
|  | 52 | MODULE_AUTHOR( | 
|  | 53 | "Martin Peschke, IBM Deutschland Entwicklung GmbH " | 
|  | 54 | "<mpeschke@de.ibm.com>"); | 
|  | 55 |  | 
|  | 56 | MODULE_DESCRIPTION( | 
|  | 57 | "identify this operating system instance to the S/390 " | 
|  | 58 | "or zSeries hardware"); | 
|  | 59 |  | 
|  | 60 | static char *system_name = NULL; | 
|  | 61 | module_param(system_name, charp, 0); | 
|  | 62 | MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); | 
|  | 63 |  | 
|  | 64 | static char *sysplex_name = NULL; | 
|  | 65 | #ifdef ALLOW_SYSPLEX_NAME | 
|  | 66 | module_param(sysplex_name, charp, 0); | 
|  | 67 | MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); | 
|  | 68 | #endif | 
|  | 69 |  | 
|  | 70 | /* use default value for this field (as well as for system level) */ | 
|  | 71 | static char *system_type = "LINUX"; | 
|  | 72 |  | 
|  | 73 | static int | 
|  | 74 | cpi_check_parms(void) | 
|  | 75 | { | 
|  | 76 | /* reject if no system type specified */ | 
|  | 77 | if (!system_type) { | 
|  | 78 | printk("cpi: bug: no system type specified\n"); | 
|  | 79 | return -EINVAL; | 
|  | 80 | } | 
|  | 81 |  | 
|  | 82 | /* reject if system type larger than 8 characters */ | 
|  | 83 | if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { | 
|  | 84 | printk("cpi: bug: system type has length of %li characters - " | 
|  | 85 | "only %i characters supported\n", | 
|  | 86 | strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); | 
|  | 87 | return -EINVAL; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | /* reject if no system name specified */ | 
|  | 91 | if (!system_name) { | 
|  | 92 | printk("cpi: no system name specified\n"); | 
|  | 93 | return -EINVAL; | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | /* reject if system name larger than 8 characters */ | 
|  | 97 | if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { | 
|  | 98 | printk("cpi: system name has length of %li characters - " | 
|  | 99 | "only %i characters supported\n", | 
|  | 100 | strlen(system_name), CPI_LENGTH_SYSTEM_NAME); | 
|  | 101 | return -EINVAL; | 
|  | 102 | } | 
|  | 103 |  | 
|  | 104 | /* reject if specified sysplex name larger than 8 characters */ | 
|  | 105 | if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { | 
|  | 106 | printk("cpi: sysplex name has length of %li characters" | 
|  | 107 | " - only %i characters supported\n", | 
|  | 108 | strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); | 
|  | 109 | return -EINVAL; | 
|  | 110 | } | 
|  | 111 | return 0; | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | static void | 
|  | 115 | cpi_callback(struct sclp_req *req, void *data) | 
|  | 116 | { | 
|  | 117 | struct semaphore *sem; | 
|  | 118 |  | 
|  | 119 | sem = (struct semaphore *) data; | 
|  | 120 | up(sem); | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | static struct sclp_req * | 
|  | 124 | cpi_prepare_req(void) | 
|  | 125 | { | 
|  | 126 | struct sclp_req *req; | 
|  | 127 | struct cpi_sccb *sccb; | 
|  | 128 | struct cpi_evbuf *evb; | 
|  | 129 |  | 
|  | 130 | req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL); | 
|  | 131 | if (req == NULL) | 
|  | 132 | return ERR_PTR(-ENOMEM); | 
|  | 133 | sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA); | 
|  | 134 | if (sccb == NULL) { | 
|  | 135 | kfree(req); | 
|  | 136 | return ERR_PTR(-ENOMEM); | 
|  | 137 | } | 
|  | 138 | memset(sccb, 0, sizeof(struct cpi_sccb)); | 
|  | 139 |  | 
|  | 140 | /* setup SCCB for Control-Program Identification */ | 
|  | 141 | sccb->header.length = sizeof(struct cpi_sccb); | 
|  | 142 | sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); | 
|  | 143 | sccb->cpi_evbuf.header.type = 0x0B; | 
|  | 144 | evb = &sccb->cpi_evbuf; | 
|  | 145 |  | 
|  | 146 | /* set system type */ | 
|  | 147 | memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); | 
|  | 148 | memcpy(evb->system_type, system_type, strlen(system_type)); | 
|  | 149 | sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); | 
|  | 150 | EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); | 
|  | 151 |  | 
|  | 152 | /* set system name */ | 
|  | 153 | memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); | 
|  | 154 | memcpy(evb->system_name, system_name, strlen(system_name)); | 
|  | 155 | sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); | 
|  | 156 | EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); | 
|  | 157 |  | 
|  | 158 | /* set sytem level */ | 
|  | 159 | evb->system_level = LINUX_VERSION_CODE; | 
|  | 160 |  | 
|  | 161 | /* set sysplex name */ | 
|  | 162 | if (sysplex_name) { | 
|  | 163 | memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); | 
|  | 164 | memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); | 
|  | 165 | sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); | 
|  | 166 | EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | /* prepare request data structure presented to SCLP driver */ | 
|  | 170 | req->command = SCLP_CMDW_WRITEDATA; | 
|  | 171 | req->sccb = sccb; | 
|  | 172 | req->status = SCLP_REQ_FILLED; | 
|  | 173 | req->callback = cpi_callback; | 
|  | 174 | return req; | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | static void | 
|  | 178 | cpi_free_req(struct sclp_req *req) | 
|  | 179 | { | 
|  | 180 | free_page((unsigned long) req->sccb); | 
|  | 181 | kfree(req); | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | static int __init | 
|  | 185 | cpi_module_init(void) | 
|  | 186 | { | 
|  | 187 | struct semaphore sem; | 
|  | 188 | struct sclp_req *req; | 
|  | 189 | int rc; | 
|  | 190 |  | 
|  | 191 | rc = cpi_check_parms(); | 
|  | 192 | if (rc) | 
|  | 193 | return rc; | 
|  | 194 |  | 
|  | 195 | rc = sclp_register(&sclp_cpi_event); | 
|  | 196 | if (rc) { | 
|  | 197 | /* could not register sclp event. Die. */ | 
|  | 198 | printk(KERN_WARNING "cpi: could not register to hardware " | 
|  | 199 | "console.\n"); | 
|  | 200 | return -EINVAL; | 
|  | 201 | } | 
|  | 202 | if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { | 
|  | 203 | printk(KERN_WARNING "cpi: no control program identification " | 
|  | 204 | "support\n"); | 
|  | 205 | sclp_unregister(&sclp_cpi_event); | 
| Peter Oberparleiter | 9bbc834 | 2006-01-06 00:19:27 -0800 | [diff] [blame] | 206 | return -EOPNOTSUPP; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | } | 
|  | 208 |  | 
|  | 209 | req = cpi_prepare_req(); | 
|  | 210 | if (IS_ERR(req)) { | 
|  | 211 | printk(KERN_WARNING "cpi: couldn't allocate request\n"); | 
|  | 212 | sclp_unregister(&sclp_cpi_event); | 
|  | 213 | return PTR_ERR(req); | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | /* Prepare semaphore */ | 
|  | 217 | sema_init(&sem, 0); | 
|  | 218 | req->callback_data = &sem; | 
|  | 219 | /* Add request to sclp queue */ | 
|  | 220 | rc = sclp_add_request(req); | 
|  | 221 | if (rc) { | 
|  | 222 | printk(KERN_WARNING "cpi: could not start request\n"); | 
|  | 223 | cpi_free_req(req); | 
|  | 224 | sclp_unregister(&sclp_cpi_event); | 
|  | 225 | return rc; | 
|  | 226 | } | 
|  | 227 | /* make "insmod" sleep until callback arrives */ | 
|  | 228 | down(&sem); | 
|  | 229 |  | 
|  | 230 | rc = ((struct cpi_sccb *) req->sccb)->header.response_code; | 
|  | 231 | if (rc != 0x0020) { | 
|  | 232 | printk(KERN_WARNING "cpi: failed with response code 0x%x\n", | 
|  | 233 | rc); | 
|  | 234 | rc = -ECOMM; | 
|  | 235 | } else | 
|  | 236 | rc = 0; | 
|  | 237 |  | 
|  | 238 | cpi_free_req(req); | 
|  | 239 | sclp_unregister(&sclp_cpi_event); | 
|  | 240 |  | 
|  | 241 | return rc; | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 |  | 
|  | 245 | static void __exit cpi_module_exit(void) | 
|  | 246 | { | 
|  | 247 | } | 
|  | 248 |  | 
|  | 249 |  | 
|  | 250 | /* declare driver module init/cleanup functions */ | 
|  | 251 | module_init(cpi_module_init); | 
|  | 252 | module_exit(cpi_module_exit); | 
|  | 253 |  |