1 /* 2 * IUCV special message driver 3 * 4 * Copyright 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation 5 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22 #include <linux/module.h> 23 #include <linux/init.h> 24 #include <linux/errno.h> 25 #include <linux/device.h> 26 #include <net/iucv/iucv.h> 27 #include <asm/cpcmd.h> 28 #include <asm/ebcdic.h> 29 #include "smsgiucv.h" 30 31 struct smsg_callback { 32 struct list_head list; 33 char *prefix; 34 int len; 35 void (*callback)(char *from, char *str); 36 }; 37 38 MODULE_AUTHOR 39 ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)"); 40 MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver"); 41 42 static struct iucv_path *smsg_path; 43 44 static DEFINE_SPINLOCK(smsg_list_lock); 45 static LIST_HEAD(smsg_list); 46 47 static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); 48 static void smsg_message_pending(struct iucv_path *, struct iucv_message *); 49 50 static struct iucv_handler smsg_handler = { 51 .path_pending = smsg_path_pending, 52 .message_pending = smsg_message_pending, 53 }; 54 55 static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8], 56 u8 ipuser[16]) 57 { 58 if (strncmp(ipvmid, "*MSG ", sizeof(ipvmid)) != 0) 59 return -EINVAL; 60 /* Path pending from *MSG. */ 61 return iucv_path_accept(path, &smsg_handler, "SMSGIUCV ", NULL); 62 } 63 64 static void smsg_message_pending(struct iucv_path *path, 65 struct iucv_message *msg) 66 { 67 struct smsg_callback *cb; 68 unsigned char *buffer; 69 unsigned char sender[9]; 70 int rc, i; 71 72 buffer = kmalloc(msg->length + 1, GFP_ATOMIC | GFP_DMA); 73 if (!buffer) { 74 iucv_message_reject(path, msg); 75 return; 76 } 77 rc = iucv_message_receive(path, msg, 0, buffer, msg->length, NULL); 78 if (rc == 0) { 79 buffer[msg->length] = 0; 80 EBCASC(buffer, msg->length); 81 memcpy(sender, buffer, 8); 82 sender[8] = 0; 83 /* Remove trailing whitespace from the sender name. */ 84 for (i = 7; i >= 0; i--) { 85 if (sender[i] != ' ' && sender[i] != '\t') 86 break; 87 sender[i] = 0; 88 } 89 spin_lock(&smsg_list_lock); 90 list_for_each_entry(cb, &smsg_list, list) 91 if (strncmp(buffer + 8, cb->prefix, cb->len) == 0) { 92 cb->callback(sender, buffer + 8); 93 break; 94 } 95 spin_unlock(&smsg_list_lock); 96 } 97 kfree(buffer); 98 } 99 100 int smsg_register_callback(char *prefix, 101 void (*callback)(char *from, char *str)) 102 { 103 struct smsg_callback *cb; 104 105 cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL); 106 if (!cb) 107 return -ENOMEM; 108 cb->prefix = prefix; 109 cb->len = strlen(prefix); 110 cb->callback = callback; 111 spin_lock_bh(&smsg_list_lock); 112 list_add_tail(&cb->list, &smsg_list); 113 spin_unlock_bh(&smsg_list_lock); 114 return 0; 115 } 116 117 void smsg_unregister_callback(char *prefix, 118 void (*callback)(char *from, char *str)) 119 { 120 struct smsg_callback *cb, *tmp; 121 122 spin_lock_bh(&smsg_list_lock); 123 cb = NULL; 124 list_for_each_entry(tmp, &smsg_list, list) 125 if (tmp->callback == callback && 126 strcmp(tmp->prefix, prefix) == 0) { 127 cb = tmp; 128 list_del(&cb->list); 129 break; 130 } 131 spin_unlock_bh(&smsg_list_lock); 132 kfree(cb); 133 } 134 135 static struct device_driver smsg_driver = { 136 .name = "SMSGIUCV", 137 .bus = &iucv_bus, 138 }; 139 140 static void __exit smsg_exit(void) 141 { 142 cpcmd("SET SMSG IUCV", NULL, 0, NULL); 143 iucv_unregister(&smsg_handler, 1); 144 driver_unregister(&smsg_driver); 145 } 146 147 static int __init smsg_init(void) 148 { 149 int rc; 150 151 if (!MACHINE_IS_VM) { 152 rc = -EPROTONOSUPPORT; 153 goto out; 154 } 155 rc = driver_register(&smsg_driver); 156 if (rc != 0) 157 goto out; 158 rc = iucv_register(&smsg_handler, 1); 159 if (rc) 160 goto out_driver; 161 smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL); 162 if (!smsg_path) { 163 rc = -ENOMEM; 164 goto out_register; 165 } 166 rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", 167 NULL, NULL, NULL); 168 if (rc) 169 goto out_free; 170 cpcmd("SET SMSG IUCV", NULL, 0, NULL); 171 return 0; 172 173 out_free: 174 iucv_path_free(smsg_path); 175 out_register: 176 iucv_unregister(&smsg_handler, 1); 177 out_driver: 178 driver_unregister(&smsg_driver); 179 out: 180 return rc; 181 } 182 183 module_init(smsg_init); 184 module_exit(smsg_exit); 185 MODULE_LICENSE("GPL"); 186 187 EXPORT_SYMBOL(smsg_register_callback); 188 EXPORT_SYMBOL(smsg_unregister_callback); 189