linux/drivers/s390/net/smsgiucv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * IUCV special message driver
   4 *
   5 * Copyright IBM Corp. 2003, 2009
   6 *
   7 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/errno.h>
  13#include <linux/device.h>
  14#include <linux/slab.h>
  15#include <net/iucv/iucv.h>
  16#include <asm/cpcmd.h>
  17#include <asm/ebcdic.h>
  18#include "smsgiucv.h"
  19
  20struct smsg_callback {
  21        struct list_head list;
  22        const char *prefix;
  23        int len;
  24        void (*callback)(const char *from, char *str);
  25};
  26
  27MODULE_AUTHOR
  28   ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)");
  29MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
  30
  31static struct iucv_path *smsg_path;
  32
  33static DEFINE_SPINLOCK(smsg_list_lock);
  34static LIST_HEAD(smsg_list);
  35
  36static int smsg_path_pending(struct iucv_path *, u8 *, u8 *);
  37static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
  38
  39static struct iucv_handler smsg_handler = {
  40        .path_pending    = smsg_path_pending,
  41        .message_pending = smsg_message_pending,
  42};
  43
  44static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser)
  45{
  46        if (strncmp(ipvmid, "*MSG    ", 8) != 0)
  47                return -EINVAL;
  48        /* Path pending from *MSG. */
  49        return iucv_path_accept(path, &smsg_handler, "SMSGIUCV        ", NULL);
  50}
  51
  52static void smsg_message_pending(struct iucv_path *path,
  53                                 struct iucv_message *msg)
  54{
  55        struct smsg_callback *cb;
  56        unsigned char *buffer;
  57        unsigned char sender[9];
  58        int rc, i;
  59
  60        buffer = kmalloc(msg->length + 1, GFP_ATOMIC | GFP_DMA);
  61        if (!buffer) {
  62                iucv_message_reject(path, msg);
  63                return;
  64        }
  65        rc = iucv_message_receive(path, msg, 0, buffer, msg->length, NULL);
  66        if (rc == 0) {
  67                buffer[msg->length] = 0;
  68                EBCASC(buffer, msg->length);
  69                memcpy(sender, buffer, 8);
  70                sender[8] = 0;
  71                /* Remove trailing whitespace from the sender name. */
  72                for (i = 7; i >= 0; i--) {
  73                        if (sender[i] != ' ' && sender[i] != '\t')
  74                                break;
  75                        sender[i] = 0;
  76                }
  77                spin_lock(&smsg_list_lock);
  78                list_for_each_entry(cb, &smsg_list, list)
  79                        if (strncmp(buffer + 8, cb->prefix, cb->len) == 0) {
  80                                cb->callback(sender, buffer + 8);
  81                                break;
  82                        }
  83                spin_unlock(&smsg_list_lock);
  84        }
  85        kfree(buffer);
  86}
  87
  88int smsg_register_callback(const char *prefix,
  89                           void (*callback)(const char *from, char *str))
  90{
  91        struct smsg_callback *cb;
  92
  93        cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL);
  94        if (!cb)
  95                return -ENOMEM;
  96        cb->prefix = prefix;
  97        cb->len = strlen(prefix);
  98        cb->callback = callback;
  99        spin_lock_bh(&smsg_list_lock);
 100        list_add_tail(&cb->list, &smsg_list);
 101        spin_unlock_bh(&smsg_list_lock);
 102        return 0;
 103}
 104
 105void smsg_unregister_callback(const char *prefix,
 106                              void (*callback)(const char *from,
 107                                               char *str))
 108{
 109        struct smsg_callback *cb, *tmp;
 110
 111        spin_lock_bh(&smsg_list_lock);
 112        cb = NULL;
 113        list_for_each_entry(tmp, &smsg_list, list)
 114                if (tmp->callback == callback &&
 115                    strcmp(tmp->prefix, prefix) == 0) {
 116                        cb = tmp;
 117                        list_del(&cb->list);
 118                        break;
 119                }
 120        spin_unlock_bh(&smsg_list_lock);
 121        kfree(cb);
 122}
 123
 124static struct device_driver smsg_driver = {
 125        .owner = THIS_MODULE,
 126        .name = SMSGIUCV_DRV_NAME,
 127        .bus  = &iucv_bus,
 128};
 129
 130static void __exit smsg_exit(void)
 131{
 132        cpcmd("SET SMSG OFF", NULL, 0, NULL);
 133        iucv_unregister(&smsg_handler, 1);
 134        driver_unregister(&smsg_driver);
 135}
 136
 137static int __init smsg_init(void)
 138{
 139        int rc;
 140
 141        if (!MACHINE_IS_VM) {
 142                rc = -EPROTONOSUPPORT;
 143                goto out;
 144        }
 145        rc = driver_register(&smsg_driver);
 146        if (rc != 0)
 147                goto out;
 148        rc = iucv_register(&smsg_handler, 1);
 149        if (rc)
 150                goto out_driver;
 151        smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL);
 152        if (!smsg_path) {
 153                rc = -ENOMEM;
 154                goto out_register;
 155        }
 156        rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG    ",
 157                               NULL, NULL, NULL);
 158        if (rc)
 159                goto out_free_path;
 160
 161        cpcmd("SET SMSG IUCV", NULL, 0, NULL);
 162        return 0;
 163
 164out_free_path:
 165        iucv_path_free(smsg_path);
 166        smsg_path = NULL;
 167out_register:
 168        iucv_unregister(&smsg_handler, 1);
 169out_driver:
 170        driver_unregister(&smsg_driver);
 171out:
 172        return rc;
 173}
 174
 175module_init(smsg_init);
 176module_exit(smsg_exit);
 177MODULE_LICENSE("GPL");
 178
 179EXPORT_SYMBOL(smsg_register_callback);
 180EXPORT_SYMBOL(smsg_unregister_callback);
 181