1 // SPDX-License-Identifier: GPL-2.0+
3 * IUCV special message driver
5 * Copyright IBM Corp. 2003, 2009
7 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
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>
20 struct smsg_callback
{
21 struct list_head list
;
24 void (*callback
)(const char *from
, char *str
);
28 ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)");
29 MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
31 static struct iucv_path
*smsg_path
;
33 static DEFINE_SPINLOCK(smsg_list_lock
);
34 static LIST_HEAD(smsg_list
);
36 static int smsg_path_pending(struct iucv_path
*, u8
*, u8
*);
37 static void smsg_message_pending(struct iucv_path
*, struct iucv_message
*);
39 static struct iucv_handler smsg_handler
= {
40 .path_pending
= smsg_path_pending
,
41 .message_pending
= smsg_message_pending
,
44 static int smsg_path_pending(struct iucv_path
*path
, u8
*ipvmid
, u8
*ipuser
)
46 if (strncmp(ipvmid
, "*MSG ", 8) != 0)
48 /* Path pending from *MSG. */
49 return iucv_path_accept(path
, &smsg_handler
, "SMSGIUCV ", NULL
);
52 static void smsg_message_pending(struct iucv_path
*path
,
53 struct iucv_message
*msg
)
55 struct smsg_callback
*cb
;
56 unsigned char *buffer
;
57 unsigned char sender
[9];
60 buffer
= kmalloc(msg
->length
+ 1, GFP_ATOMIC
| GFP_DMA
);
62 iucv_message_reject(path
, msg
);
65 rc
= iucv_message_receive(path
, msg
, 0, buffer
, msg
->length
, NULL
);
67 buffer
[msg
->length
] = 0;
68 EBCASC(buffer
, msg
->length
);
69 memcpy(sender
, buffer
, 8);
71 /* Remove trailing whitespace from the sender name. */
72 for (i
= 7; i
>= 0; i
--) {
73 if (sender
[i
] != ' ' && sender
[i
] != '\t')
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);
83 spin_unlock(&smsg_list_lock
);
88 int smsg_register_callback(const char *prefix
,
89 void (*callback
)(const char *from
, char *str
))
91 struct smsg_callback
*cb
;
93 cb
= kmalloc(sizeof(struct smsg_callback
), GFP_KERNEL
);
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
);
105 void smsg_unregister_callback(const char *prefix
,
106 void (*callback
)(const char *from
,
109 struct smsg_callback
*cb
, *tmp
;
111 spin_lock_bh(&smsg_list_lock
);
113 list_for_each_entry(tmp
, &smsg_list
, list
)
114 if (tmp
->callback
== callback
&&
115 strcmp(tmp
->prefix
, prefix
) == 0) {
120 spin_unlock_bh(&smsg_list_lock
);
124 static struct device_driver smsg_driver
= {
125 .owner
= THIS_MODULE
,
126 .name
= SMSGIUCV_DRV_NAME
,
130 static void __exit
smsg_exit(void)
132 cpcmd("SET SMSG OFF", NULL
, 0, NULL
);
133 iucv_unregister(&smsg_handler
, 1);
134 driver_unregister(&smsg_driver
);
137 static int __init
smsg_init(void)
141 if (!MACHINE_IS_VM
) {
142 rc
= -EPROTONOSUPPORT
;
145 rc
= driver_register(&smsg_driver
);
148 rc
= iucv_register(&smsg_handler
, 1);
151 smsg_path
= iucv_path_alloc(255, 0, GFP_KERNEL
);
156 rc
= iucv_path_connect(smsg_path
, &smsg_handler
, "*MSG ",
161 cpcmd("SET SMSG IUCV", NULL
, 0, NULL
);
165 iucv_path_free(smsg_path
);
168 iucv_unregister(&smsg_handler
, 1);
170 driver_unregister(&smsg_driver
);
175 module_init(smsg_init
);
176 module_exit(smsg_exit
);
177 MODULE_LICENSE("GPL");
179 EXPORT_SYMBOL(smsg_register_callback
);
180 EXPORT_SYMBOL(smsg_unregister_callback
);