1 /* 2 * IUCV special message driver 3 * 4 * Copyright IBM Corp. 2003, 2009 5 * 6 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include <linux/module.h> 24 #include <linux/init.h> 25 #include <linux/errno.h> 26 #include <linux/device.h> 27 #include <net/iucv/iucv.h> 28 #include <asm/cpcmd.h> 29 #include <asm/ebcdic.h> 30 #include "smsgiucv.h" 31 32 struct smsg_callback { 33 struct list_head list; 34 char *prefix; 35 int len; 36 void (*callback)(char *from, char *str); 37 }; 38 39 MODULE_AUTHOR 40 ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)"); 41 MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver"); 42 43 static struct iucv_path *smsg_path; 44 /* dummy device used as trigger for PM functions */ 45 static struct device *smsg_dev; 46 47 static DEFINE_SPINLOCK(smsg_list_lock); 48 static LIST_HEAD(smsg_list); 49 50 static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); 51 static void smsg_message_pending(struct iucv_path *, struct iucv_message *); 52 53 static struct iucv_handler smsg_handler = { 54 .path_pending = smsg_path_pending, 55 .message_pending = smsg_message_pending, 56 }; 57 58 static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8], 59 u8 ipuser[16]) 60 { 61 if (strncmp(ipvmid, "*MSG ", sizeof(ipvmid)) != 0) 62 return -EINVAL; 63 /* Path pending from *MSG. */ 64 return iucv_path_accept(path, &smsg_handler, "SMSGIUCV ", NULL); 65 } 66 67 static void smsg_message_pending(struct iucv_path *path, 68 struct iucv_message *msg) 69 { 70 struct smsg_callback *cb; 71 unsigned char *buffer; 72 unsigned char sender[9]; 73 int rc, i; 74 75 buffer = kmalloc(msg->length + 1, GFP_ATOMIC | GFP_DMA); 76 if (!buffer) { 77 iucv_message_reject(path, msg); 78 return; 79 } 80 rc = iucv_message_receive(path, msg, 0, buffer, msg->length, NULL); 81 if (rc == 0) { 82 buffer[msg->length] = 0; 83 EBCASC(buffer, msg->length); 84 memcpy(sender, buffer, 8); 85 sender[8] = 0; 86 /* Remove trailing whitespace from the sender name. */ 87 for (i = 7; i >= 0; i--) { 88 if (sender[i] != ' ' && sender[i] != '\t') 89 break; 90 sender[i] = 0; 91 } 92 spin_lock(&smsg_list_lock); 93 list_for_each_entry(cb, &smsg_list, list) 94 if (strncmp(buffer + 8, cb->prefix, cb->len) == 0) { 95 cb->callback(sender, buffer + 8); 96 break; 97 } 98 spin_unlock(&smsg_list_lock); 99 } 100 kfree(buffer); 101 } 102 103 int smsg_register_callback(char *prefix, 104 void (*callback)(char *from, char *str)) 105 { 106 struct smsg_callback *cb; 107 108 cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL); 109 if (!cb) 110 return -ENOMEM; 111 cb->prefix = prefix; 112 cb->len = strlen(prefix); 113 cb->callback = callback; 114 spin_lock_bh(&smsg_list_lock); 115 list_add_tail(&cb->list, &smsg_list); 116 spin_unlock_bh(&smsg_list_lock); 117 return 0; 118 } 119 120 void smsg_unregister_callback(char *prefix, 121 void (*callback)(char *from, char *str)) 122 { 123 struct smsg_callback *cb, *tmp; 124 125 spin_lock_bh(&smsg_list_lock); 126 cb = NULL; 127 list_for_each_entry(tmp, &smsg_list, list) 128 if (tmp->callback == callback && 129 strcmp(tmp->prefix, prefix) == 0) { 130 cb = tmp; 131 list_del(&cb->list); 132 break; 133 } 134 spin_unlock_bh(&smsg_list_lock); 135 kfree(cb); 136 } 137 138 static int smsg_pm_freeze(struct device *dev) 139 { 140 #ifdef CONFIG_PM_DEBUG 141 printk(KERN_WARNING "smsg_pm_freeze\n"); 142 #endif 143 if (smsg_path) 144 iucv_path_sever(smsg_path, NULL); 145 return 0; 146 } 147 148 static int smsg_pm_restore_thaw(struct device *dev) 149 { 150 int rc; 151 152 #ifdef CONFIG_PM_DEBUG 153 printk(KERN_WARNING "smsg_pm_restore_thaw\n"); 154 #endif 155 if (smsg_path) { 156 memset(smsg_path, 0, sizeof(*smsg_path)); 157 smsg_path->msglim = 255; 158 smsg_path->flags = 0; 159 rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", 160 NULL, NULL, NULL); 161 #ifdef CONFIG_PM_DEBUG 162 if (rc) 163 printk(KERN_ERR 164 "iucv_path_connect returned with rc %i\n", rc); 165 #endif 166 cpcmd("SET SMSG IUCV", NULL, 0, NULL); 167 } 168 return 0; 169 } 170 171 static const struct dev_pm_ops smsg_pm_ops = { 172 .freeze = smsg_pm_freeze, 173 .thaw = smsg_pm_restore_thaw, 174 .restore = smsg_pm_restore_thaw, 175 }; 176 177 static struct device_driver smsg_driver = { 178 .owner = THIS_MODULE, 179 .name = "SMSGIUCV", 180 .bus = &iucv_bus, 181 .pm = &smsg_pm_ops, 182 }; 183 184 static void __exit smsg_exit(void) 185 { 186 cpcmd("SET SMSG IUCV", NULL, 0, NULL); 187 device_unregister(smsg_dev); 188 iucv_unregister(&smsg_handler, 1); 189 driver_unregister(&smsg_driver); 190 } 191 192 static int __init smsg_init(void) 193 { 194 int rc; 195 196 if (!MACHINE_IS_VM) { 197 rc = -EPROTONOSUPPORT; 198 goto out; 199 } 200 rc = driver_register(&smsg_driver); 201 if (rc != 0) 202 goto out; 203 rc = iucv_register(&smsg_handler, 1); 204 if (rc) 205 goto out_driver; 206 smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL); 207 if (!smsg_path) { 208 rc = -ENOMEM; 209 goto out_register; 210 } 211 rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", 212 NULL, NULL, NULL); 213 if (rc) 214 goto out_free_path; 215 smsg_dev = kzalloc(sizeof(struct device), GFP_KERNEL); 216 if (!smsg_dev) { 217 rc = -ENOMEM; 218 goto out_free_path; 219 } 220 dev_set_name(smsg_dev, "smsg_iucv"); 221 smsg_dev->bus = &iucv_bus; 222 smsg_dev->parent = iucv_root; 223 smsg_dev->release = (void (*)(struct device *))kfree; 224 smsg_dev->driver = &smsg_driver; 225 rc = device_register(smsg_dev); 226 if (rc) 227 goto out_put; 228 229 cpcmd("SET SMSG IUCV", NULL, 0, NULL); 230 return 0; 231 232 out_put: 233 put_device(smsg_dev); 234 out_free_path: 235 iucv_path_free(smsg_path); 236 smsg_path = NULL; 237 out_register: 238 iucv_unregister(&smsg_handler, 1); 239 out_driver: 240 driver_unregister(&smsg_driver); 241 out: 242 return rc; 243 } 244 245 module_init(smsg_init); 246 module_exit(smsg_exit); 247 MODULE_LICENSE("GPL"); 248 249 EXPORT_SYMBOL(smsg_register_callback); 250 EXPORT_SYMBOL(smsg_unregister_callback); 251