Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * IUCV special message driver
0004  *
0005  * Copyright IBM Corp. 2003, 2009
0006  *
0007  * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/init.h>
0012 #include <linux/errno.h>
0013 #include <linux/device.h>
0014 #include <linux/slab.h>
0015 #include <net/iucv/iucv.h>
0016 #include <asm/cpcmd.h>
0017 #include <asm/ebcdic.h>
0018 #include "smsgiucv.h"
0019 
0020 struct smsg_callback {
0021     struct list_head list;
0022     const char *prefix;
0023     int len;
0024     void (*callback)(const char *from, char *str);
0025 };
0026 
0027 MODULE_AUTHOR
0028    ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)");
0029 MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
0030 
0031 static struct iucv_path *smsg_path;
0032 
0033 static DEFINE_SPINLOCK(smsg_list_lock);
0034 static LIST_HEAD(smsg_list);
0035 
0036 static int smsg_path_pending(struct iucv_path *, u8 *, u8 *);
0037 static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
0038 
0039 static struct iucv_handler smsg_handler = {
0040     .path_pending    = smsg_path_pending,
0041     .message_pending = smsg_message_pending,
0042 };
0043 
0044 static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser)
0045 {
0046     if (strncmp(ipvmid, "*MSG    ", 8) != 0)
0047         return -EINVAL;
0048     /* Path pending from *MSG. */
0049     return iucv_path_accept(path, &smsg_handler, "SMSGIUCV        ", NULL);
0050 }
0051 
0052 static void smsg_message_pending(struct iucv_path *path,
0053                  struct iucv_message *msg)
0054 {
0055     struct smsg_callback *cb;
0056     unsigned char *buffer;
0057     unsigned char sender[9];
0058     int rc, i;
0059 
0060     buffer = kmalloc(msg->length + 1, GFP_ATOMIC | GFP_DMA);
0061     if (!buffer) {
0062         iucv_message_reject(path, msg);
0063         return;
0064     }
0065     rc = iucv_message_receive(path, msg, 0, buffer, msg->length, NULL);
0066     if (rc == 0) {
0067         buffer[msg->length] = 0;
0068         EBCASC(buffer, msg->length);
0069         memcpy(sender, buffer, 8);
0070         sender[8] = 0;
0071         /* Remove trailing whitespace from the sender name. */
0072         for (i = 7; i >= 0; i--) {
0073             if (sender[i] != ' ' && sender[i] != '\t')
0074                 break;
0075             sender[i] = 0;
0076         }
0077         spin_lock(&smsg_list_lock);
0078         list_for_each_entry(cb, &smsg_list, list)
0079             if (strncmp(buffer + 8, cb->prefix, cb->len) == 0) {
0080                 cb->callback(sender, buffer + 8);
0081                 break;
0082             }
0083         spin_unlock(&smsg_list_lock);
0084     }
0085     kfree(buffer);
0086 }
0087 
0088 int smsg_register_callback(const char *prefix,
0089                void (*callback)(const char *from, char *str))
0090 {
0091     struct smsg_callback *cb;
0092 
0093     cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL);
0094     if (!cb)
0095         return -ENOMEM;
0096     cb->prefix = prefix;
0097     cb->len = strlen(prefix);
0098     cb->callback = callback;
0099     spin_lock_bh(&smsg_list_lock);
0100     list_add_tail(&cb->list, &smsg_list);
0101     spin_unlock_bh(&smsg_list_lock);
0102     return 0;
0103 }
0104 
0105 void smsg_unregister_callback(const char *prefix,
0106                   void (*callback)(const char *from,
0107                            char *str))
0108 {
0109     struct smsg_callback *cb, *tmp;
0110 
0111     spin_lock_bh(&smsg_list_lock);
0112     cb = NULL;
0113     list_for_each_entry(tmp, &smsg_list, list)
0114         if (tmp->callback == callback &&
0115             strcmp(tmp->prefix, prefix) == 0) {
0116             cb = tmp;
0117             list_del(&cb->list);
0118             break;
0119         }
0120     spin_unlock_bh(&smsg_list_lock);
0121     kfree(cb);
0122 }
0123 
0124 static struct device_driver smsg_driver = {
0125     .owner = THIS_MODULE,
0126     .name = SMSGIUCV_DRV_NAME,
0127     .bus  = &iucv_bus,
0128 };
0129 
0130 static void __exit smsg_exit(void)
0131 {
0132     cpcmd("SET SMSG OFF", NULL, 0, NULL);
0133     iucv_unregister(&smsg_handler, 1);
0134     driver_unregister(&smsg_driver);
0135 }
0136 
0137 static int __init smsg_init(void)
0138 {
0139     int rc;
0140 
0141     if (!MACHINE_IS_VM) {
0142         rc = -EPROTONOSUPPORT;
0143         goto out;
0144     }
0145     rc = driver_register(&smsg_driver);
0146     if (rc != 0)
0147         goto out;
0148     rc = iucv_register(&smsg_handler, 1);
0149     if (rc)
0150         goto out_driver;
0151     smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL);
0152     if (!smsg_path) {
0153         rc = -ENOMEM;
0154         goto out_register;
0155     }
0156     rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG    ",
0157                    NULL, NULL, NULL);
0158     if (rc)
0159         goto out_free_path;
0160 
0161     cpcmd("SET SMSG IUCV", NULL, 0, NULL);
0162     return 0;
0163 
0164 out_free_path:
0165     iucv_path_free(smsg_path);
0166     smsg_path = NULL;
0167 out_register:
0168     iucv_unregister(&smsg_handler, 1);
0169 out_driver:
0170     driver_unregister(&smsg_driver);
0171 out:
0172     return rc;
0173 }
0174 
0175 module_init(smsg_init);
0176 module_exit(smsg_exit);
0177 MODULE_LICENSE("GPL");
0178 
0179 EXPORT_SYMBOL(smsg_register_callback);
0180 EXPORT_SYMBOL(smsg_unregister_callback);