xref: /linux/drivers/tty/hvc/hvc_iucv.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
244a01d5bSHendrik Brueckner /*
3926a7336SHendrik Brueckner  * z/VM IUCV hypervisor console (HVC) device driver
444a01d5bSHendrik Brueckner  *
517e19f04SHendrik Brueckner  * This HVC device driver provides terminal access using
644a01d5bSHendrik Brueckner  * z/VM IUCV communication paths.
744a01d5bSHendrik Brueckner  *
8926a7336SHendrik Brueckner  * Copyright IBM Corp. 2008, 2013
944a01d5bSHendrik Brueckner  *
1044a01d5bSHendrik Brueckner  * Author(s):	Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
1144a01d5bSHendrik Brueckner  */
1244a01d5bSHendrik Brueckner #define KMSG_COMPONENT		"hvc_iucv"
1317e19f04SHendrik Brueckner #define pr_fmt(fmt)		KMSG_COMPONENT ": " fmt
1444a01d5bSHendrik Brueckner 
1544a01d5bSHendrik Brueckner #include <linux/types.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
1744a01d5bSHendrik Brueckner #include <asm/ebcdic.h>
18431429ffSHendrik Brueckner #include <linux/ctype.h>
19c45ce4b5SHendrik Brueckner #include <linux/delay.h>
200259162eSHendrik Brueckner #include <linux/device.h>
2168c6b3d2SHendrik Brueckner #include <linux/init.h>
2244a01d5bSHendrik Brueckner #include <linux/mempool.h>
23431429ffSHendrik Brueckner #include <linux/moduleparam.h>
2444a01d5bSHendrik Brueckner #include <linux/tty.h>
25c45ce4b5SHendrik Brueckner #include <linux/wait.h>
2644a01d5bSHendrik Brueckner #include <net/iucv/iucv.h>
2744a01d5bSHendrik Brueckner 
2844a01d5bSHendrik Brueckner #include "hvc_console.h"
2944a01d5bSHendrik Brueckner 
3044a01d5bSHendrik Brueckner 
3117e19f04SHendrik Brueckner /* General device driver settings */
3244a01d5bSHendrik Brueckner #define HVC_IUCV_MAGIC		0xc9e4c3e5
3344a01d5bSHendrik Brueckner #define MAX_HVC_IUCV_LINES	HVC_ALLOC_TTY_ADAPTERS
3444a01d5bSHendrik Brueckner #define MEMPOOL_MIN_NR		(PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4)
3544a01d5bSHendrik Brueckner 
3644a01d5bSHendrik Brueckner /* IUCV TTY message  */
3744a01d5bSHendrik Brueckner #define MSG_VERSION		0x02	/* Message version */
3844a01d5bSHendrik Brueckner #define MSG_TYPE_ERROR		0x01	/* Error message */
3944a01d5bSHendrik Brueckner #define MSG_TYPE_TERMENV	0x02	/* Terminal environment variable */
4044a01d5bSHendrik Brueckner #define MSG_TYPE_TERMIOS	0x04	/* Terminal IO struct update */
4144a01d5bSHendrik Brueckner #define MSG_TYPE_WINSIZE	0x08	/* Terminal window size update */
4244a01d5bSHendrik Brueckner #define MSG_TYPE_DATA		0x10	/* Terminal data */
4344a01d5bSHendrik Brueckner 
4444a01d5bSHendrik Brueckner struct iucv_tty_msg {
4544a01d5bSHendrik Brueckner 	u8	version;		/* Message version */
4644a01d5bSHendrik Brueckner 	u8	type;			/* Message type */
47c45ce4b5SHendrik Brueckner #define MSG_MAX_DATALEN		((u16)(~0))
4844a01d5bSHendrik Brueckner 	u16	datalen;		/* Payload length */
4944a01d5bSHendrik Brueckner 	u8	data[];			/* Payload buffer */
5044a01d5bSHendrik Brueckner } __attribute__((packed));
5117e19f04SHendrik Brueckner #define MSG_SIZE(s)		((s) + offsetof(struct iucv_tty_msg, data))
5244a01d5bSHendrik Brueckner 
5344a01d5bSHendrik Brueckner enum iucv_state_t {
5444a01d5bSHendrik Brueckner 	IUCV_DISCONN	= 0,
5544a01d5bSHendrik Brueckner 	IUCV_CONNECTED	= 1,
5644a01d5bSHendrik Brueckner 	IUCV_SEVERED	= 2,
5744a01d5bSHendrik Brueckner };
5844a01d5bSHendrik Brueckner 
5944a01d5bSHendrik Brueckner enum tty_state_t {
6044a01d5bSHendrik Brueckner 	TTY_CLOSED	= 0,
6144a01d5bSHendrik Brueckner 	TTY_OPENED	= 1,
6244a01d5bSHendrik Brueckner };
6344a01d5bSHendrik Brueckner 
6444a01d5bSHendrik Brueckner struct hvc_iucv_private {
6517e19f04SHendrik Brueckner 	struct hvc_struct	*hvc;		/* HVC struct reference */
6644a01d5bSHendrik Brueckner 	u8			srv_name[8];	/* IUCV service name (ebcdic) */
676c089fd3SHendrik Brueckner 	unsigned char		is_console;	/* Linux console usage flag */
6844a01d5bSHendrik Brueckner 	enum iucv_state_t	iucv_state;	/* IUCV connection status */
6944a01d5bSHendrik Brueckner 	enum tty_state_t	tty_state;	/* TTY status */
7044a01d5bSHendrik Brueckner 	struct iucv_path	*path;		/* IUCV path pointer */
7144a01d5bSHendrik Brueckner 	spinlock_t		lock;		/* hvc_iucv_private lock */
72c45ce4b5SHendrik Brueckner #define SNDBUF_SIZE		(PAGE_SIZE)	/* must be < MSG_MAX_DATALEN */
73c45ce4b5SHendrik Brueckner 	void			*sndbuf;	/* send buffer		  */
74c45ce4b5SHendrik Brueckner 	size_t			sndbuf_len;	/* length of send buffer  */
75c45ce4b5SHendrik Brueckner #define QUEUE_SNDBUF_DELAY	(HZ / 25)
76c45ce4b5SHendrik Brueckner 	struct delayed_work	sndbuf_work;	/* work: send iucv msg(s) */
77c45ce4b5SHendrik Brueckner 	wait_queue_head_t	sndbuf_waitq;	/* wait for send completion */
7844a01d5bSHendrik Brueckner 	struct list_head	tty_outqueue;	/* outgoing IUCV messages */
7944a01d5bSHendrik Brueckner 	struct list_head	tty_inqueue;	/* incoming IUCV messages */
800259162eSHendrik Brueckner 	struct device		*dev;		/* device structure */
81f1206badSHendrik Brueckner 	u8			info_path[16];	/* IUCV path info (dev attr) */
8244a01d5bSHendrik Brueckner };
8344a01d5bSHendrik Brueckner 
8444a01d5bSHendrik Brueckner struct iucv_tty_buffer {
8544a01d5bSHendrik Brueckner 	struct list_head	list;	/* list pointer */
8617e19f04SHendrik Brueckner 	struct iucv_message	msg;	/* store an IUCV message */
8744a01d5bSHendrik Brueckner 	size_t			offset;	/* data buffer offset */
8844a01d5bSHendrik Brueckner 	struct iucv_tty_msg	*mbuf;	/* buffer to store input/output data */
8944a01d5bSHendrik Brueckner };
9044a01d5bSHendrik Brueckner 
9144a01d5bSHendrik Brueckner /* IUCV callback handler */
9291e60eb6SUrsula Braun static	int hvc_iucv_path_pending(struct iucv_path *, u8 *, u8 *);
9391e60eb6SUrsula Braun static void hvc_iucv_path_severed(struct iucv_path *, u8 *);
9444a01d5bSHendrik Brueckner static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *);
9544a01d5bSHendrik Brueckner static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *);
9644a01d5bSHendrik Brueckner 
9744a01d5bSHendrik Brueckner 
982dc184c0SHendrik Brueckner /* Kernel module parameter: use one terminal device as default */
992dc184c0SHendrik Brueckner static unsigned long hvc_iucv_devices = 1;
10044a01d5bSHendrik Brueckner 
10144a01d5bSHendrik Brueckner /* Array of allocated hvc iucv tty lines... */
10244a01d5bSHendrik Brueckner static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES];
1036c089fd3SHendrik Brueckner #define IUCV_HVC_CON_IDX	(0)
104431429ffSHendrik Brueckner /* List of z/VM user ID filter entries (struct iucv_vmid_filter) */
105431429ffSHendrik Brueckner #define MAX_VMID_FILTER		(500)
106926a7336SHendrik Brueckner #define FILTER_WILDCARD_CHAR	'*'
107431429ffSHendrik Brueckner static size_t hvc_iucv_filter_size;
108431429ffSHendrik Brueckner static void *hvc_iucv_filter;
109431429ffSHendrik Brueckner static const char *hvc_iucv_filter_string;
110431429ffSHendrik Brueckner static DEFINE_RWLOCK(hvc_iucv_filter_lock);
11144a01d5bSHendrik Brueckner 
11244a01d5bSHendrik Brueckner /* Kmem cache and mempool for iucv_tty_buffer elements */
11344a01d5bSHendrik Brueckner static struct kmem_cache *hvc_iucv_buffer_cache;
11444a01d5bSHendrik Brueckner static mempool_t *hvc_iucv_mempool;
11544a01d5bSHendrik Brueckner 
11644a01d5bSHendrik Brueckner /* IUCV handler callback functions */
11744a01d5bSHendrik Brueckner static struct iucv_handler hvc_iucv_handler = {
11844a01d5bSHendrik Brueckner 	.path_pending  = hvc_iucv_path_pending,
11944a01d5bSHendrik Brueckner 	.path_severed  = hvc_iucv_path_severed,
12044a01d5bSHendrik Brueckner 	.message_complete = hvc_iucv_msg_complete,
12144a01d5bSHendrik Brueckner 	.message_pending  = hvc_iucv_msg_pending,
12244a01d5bSHendrik Brueckner };
12344a01d5bSHendrik Brueckner 
12444a01d5bSHendrik Brueckner 
12544a01d5bSHendrik Brueckner /**
12644a01d5bSHendrik Brueckner  * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance.
12744a01d5bSHendrik Brueckner  * @num:	The HVC virtual terminal number (vtermno)
12844a01d5bSHendrik Brueckner  *
12944a01d5bSHendrik Brueckner  * This function returns the struct hvc_iucv_private instance that corresponds
13044a01d5bSHendrik Brueckner  * to the HVC virtual terminal number specified as parameter @num.
13144a01d5bSHendrik Brueckner  */
1322ee13c6eSHendrik Brueckner static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
13344a01d5bSHendrik Brueckner {
13444a01d5bSHendrik Brueckner 	if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices))
13544a01d5bSHendrik Brueckner 		return NULL;
13644a01d5bSHendrik Brueckner 	return hvc_iucv_table[num - HVC_IUCV_MAGIC];
13744a01d5bSHendrik Brueckner }
13844a01d5bSHendrik Brueckner 
13944a01d5bSHendrik Brueckner /**
14017e19f04SHendrik Brueckner  * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element.
14144a01d5bSHendrik Brueckner  * @size:	Size of the internal buffer used to store data.
14244a01d5bSHendrik Brueckner  * @flags:	Memory allocation flags passed to mempool.
14344a01d5bSHendrik Brueckner  *
14444a01d5bSHendrik Brueckner  * This function allocates a new struct iucv_tty_buffer element and, optionally,
14544a01d5bSHendrik Brueckner  * allocates an internal data buffer with the specified size @size.
14691a970d9SHendrik Brueckner  * The internal data buffer is always allocated with GFP_DMA which is
14791a970d9SHendrik Brueckner  * required for receiving and sending data with IUCV.
14844a01d5bSHendrik Brueckner  * Note: The total message size arises from the internal buffer size and the
14944a01d5bSHendrik Brueckner  *	 members of the iucv_tty_msg structure.
15044a01d5bSHendrik Brueckner  * The function returns NULL if memory allocation has failed.
15144a01d5bSHendrik Brueckner  */
15244a01d5bSHendrik Brueckner static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags)
15344a01d5bSHendrik Brueckner {
15444a01d5bSHendrik Brueckner 	struct iucv_tty_buffer *bufp;
15544a01d5bSHendrik Brueckner 
15644a01d5bSHendrik Brueckner 	bufp = mempool_alloc(hvc_iucv_mempool, flags);
15744a01d5bSHendrik Brueckner 	if (!bufp)
15844a01d5bSHendrik Brueckner 		return NULL;
1596c089fd3SHendrik Brueckner 	memset(bufp, 0, sizeof(*bufp));
16044a01d5bSHendrik Brueckner 
16144a01d5bSHendrik Brueckner 	if (size > 0) {
16244a01d5bSHendrik Brueckner 		bufp->msg.length = MSG_SIZE(size);
16391a970d9SHendrik Brueckner 		bufp->mbuf = kmalloc(bufp->msg.length, flags | GFP_DMA);
16444a01d5bSHendrik Brueckner 		if (!bufp->mbuf) {
16544a01d5bSHendrik Brueckner 			mempool_free(bufp, hvc_iucv_mempool);
16644a01d5bSHendrik Brueckner 			return NULL;
16744a01d5bSHendrik Brueckner 		}
16844a01d5bSHendrik Brueckner 		bufp->mbuf->version = MSG_VERSION;
16944a01d5bSHendrik Brueckner 		bufp->mbuf->type    = MSG_TYPE_DATA;
17044a01d5bSHendrik Brueckner 		bufp->mbuf->datalen = (u16) size;
17144a01d5bSHendrik Brueckner 	}
17244a01d5bSHendrik Brueckner 	return bufp;
17344a01d5bSHendrik Brueckner }
17444a01d5bSHendrik Brueckner 
17544a01d5bSHendrik Brueckner /**
17644a01d5bSHendrik Brueckner  * destroy_tty_buffer() - destroy struct iucv_tty_buffer element.
17744a01d5bSHendrik Brueckner  * @bufp:	Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL.
17844a01d5bSHendrik Brueckner  */
17944a01d5bSHendrik Brueckner static void destroy_tty_buffer(struct iucv_tty_buffer *bufp)
18044a01d5bSHendrik Brueckner {
18144a01d5bSHendrik Brueckner 	kfree(bufp->mbuf);
18244a01d5bSHendrik Brueckner 	mempool_free(bufp, hvc_iucv_mempool);
18344a01d5bSHendrik Brueckner }
18444a01d5bSHendrik Brueckner 
18544a01d5bSHendrik Brueckner /**
18644a01d5bSHendrik Brueckner  * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element.
18717e19f04SHendrik Brueckner  * @list:	List containing struct iucv_tty_buffer elements.
18844a01d5bSHendrik Brueckner  */
18944a01d5bSHendrik Brueckner static void destroy_tty_buffer_list(struct list_head *list)
19044a01d5bSHendrik Brueckner {
19144a01d5bSHendrik Brueckner 	struct iucv_tty_buffer *ent, *next;
19244a01d5bSHendrik Brueckner 
19344a01d5bSHendrik Brueckner 	list_for_each_entry_safe(ent, next, list, list) {
19444a01d5bSHendrik Brueckner 		list_del(&ent->list);
19544a01d5bSHendrik Brueckner 		destroy_tty_buffer(ent);
19644a01d5bSHendrik Brueckner 	}
19744a01d5bSHendrik Brueckner }
19844a01d5bSHendrik Brueckner 
19944a01d5bSHendrik Brueckner /**
20017e19f04SHendrik Brueckner  * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer.
20117e19f04SHendrik Brueckner  * @priv:		Pointer to struct hvc_iucv_private
20217e19f04SHendrik Brueckner  * @buf:		HVC buffer for writing received terminal data.
20317e19f04SHendrik Brueckner  * @count:		HVC buffer size.
20444a01d5bSHendrik Brueckner  * @has_more_data:	Pointer to an int variable.
20544a01d5bSHendrik Brueckner  *
20644a01d5bSHendrik Brueckner  * The function picks up pending messages from the input queue and receives
20744a01d5bSHendrik Brueckner  * the message data that is then written to the specified buffer @buf.
20817e19f04SHendrik Brueckner  * If the buffer size @count is less than the data message size, the
20944a01d5bSHendrik Brueckner  * message is kept on the input queue and @has_more_data is set to 1.
21017e19f04SHendrik Brueckner  * If all message data has been written, the message is removed from
21144a01d5bSHendrik Brueckner  * the input queue.
21244a01d5bSHendrik Brueckner  *
21344a01d5bSHendrik Brueckner  * The function returns the number of bytes written to the terminal, zero if
21444a01d5bSHendrik Brueckner  * there are no pending data messages available or if there is no established
21544a01d5bSHendrik Brueckner  * IUCV path.
21644a01d5bSHendrik Brueckner  * If the IUCV path has been severed, then -EPIPE is returned to cause a
21717e19f04SHendrik Brueckner  * hang up (that is issued by the HVC layer).
21844a01d5bSHendrik Brueckner  */
21944a01d5bSHendrik Brueckner static int hvc_iucv_write(struct hvc_iucv_private *priv,
22044a01d5bSHendrik Brueckner 			  char *buf, int count, int *has_more_data)
22144a01d5bSHendrik Brueckner {
22244a01d5bSHendrik Brueckner 	struct iucv_tty_buffer *rb;
22344a01d5bSHendrik Brueckner 	int written;
22444a01d5bSHendrik Brueckner 	int rc;
22544a01d5bSHendrik Brueckner 
22617e19f04SHendrik Brueckner 	/* immediately return if there is no IUCV connection */
22744a01d5bSHendrik Brueckner 	if (priv->iucv_state == IUCV_DISCONN)
22844a01d5bSHendrik Brueckner 		return 0;
22944a01d5bSHendrik Brueckner 
23017e19f04SHendrik Brueckner 	/* if the IUCV path has been severed, return -EPIPE to inform the
23117e19f04SHendrik Brueckner 	 * HVC layer to hang up the tty device. */
23244a01d5bSHendrik Brueckner 	if (priv->iucv_state == IUCV_SEVERED)
23344a01d5bSHendrik Brueckner 		return -EPIPE;
23444a01d5bSHendrik Brueckner 
23544a01d5bSHendrik Brueckner 	/* check if there are pending messages */
23644a01d5bSHendrik Brueckner 	if (list_empty(&priv->tty_inqueue))
23744a01d5bSHendrik Brueckner 		return 0;
23844a01d5bSHendrik Brueckner 
23917e19f04SHendrik Brueckner 	/* receive an iucv message and flip data to the tty (ldisc) */
24044a01d5bSHendrik Brueckner 	rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list);
24144a01d5bSHendrik Brueckner 
24244a01d5bSHendrik Brueckner 	written = 0;
24344a01d5bSHendrik Brueckner 	if (!rb->mbuf) { /* message not yet received ... */
24444a01d5bSHendrik Brueckner 		/* allocate mem to store msg data; if no memory is available
24544a01d5bSHendrik Brueckner 		 * then leave the buffer on the list and re-try later */
24691a970d9SHendrik Brueckner 		rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA);
24744a01d5bSHendrik Brueckner 		if (!rb->mbuf)
24844a01d5bSHendrik Brueckner 			return -ENOMEM;
24944a01d5bSHendrik Brueckner 
25044a01d5bSHendrik Brueckner 		rc = __iucv_message_receive(priv->path, &rb->msg, 0,
25144a01d5bSHendrik Brueckner 					    rb->mbuf, rb->msg.length, NULL);
25244a01d5bSHendrik Brueckner 		switch (rc) {
25344a01d5bSHendrik Brueckner 		case 0: /* Successful	    */
25444a01d5bSHendrik Brueckner 			break;
25544a01d5bSHendrik Brueckner 		case 2:	/* No message found */
25644a01d5bSHendrik Brueckner 		case 9: /* Message purged   */
25744a01d5bSHendrik Brueckner 			break;
25844a01d5bSHendrik Brueckner 		default:
25944a01d5bSHendrik Brueckner 			written = -EIO;
26044a01d5bSHendrik Brueckner 		}
26125985edcSLucas De Marchi 		/* remove buffer if an error has occurred or received data
26244a01d5bSHendrik Brueckner 		 * is not correct */
26344a01d5bSHendrik Brueckner 		if (rc || (rb->mbuf->version != MSG_VERSION) ||
26444a01d5bSHendrik Brueckner 			  (rb->msg.length    != MSG_SIZE(rb->mbuf->datalen)))
26544a01d5bSHendrik Brueckner 			goto out_remove_buffer;
26644a01d5bSHendrik Brueckner 	}
26744a01d5bSHendrik Brueckner 
26844a01d5bSHendrik Brueckner 	switch (rb->mbuf->type) {
26944a01d5bSHendrik Brueckner 	case MSG_TYPE_DATA:
27044a01d5bSHendrik Brueckner 		written = min_t(int, rb->mbuf->datalen - rb->offset, count);
27144a01d5bSHendrik Brueckner 		memcpy(buf, rb->mbuf->data + rb->offset, written);
27244a01d5bSHendrik Brueckner 		if (written < (rb->mbuf->datalen - rb->offset)) {
27344a01d5bSHendrik Brueckner 			rb->offset += written;
27444a01d5bSHendrik Brueckner 			*has_more_data = 1;
27544a01d5bSHendrik Brueckner 			goto out_written;
27644a01d5bSHendrik Brueckner 		}
27744a01d5bSHendrik Brueckner 		break;
27844a01d5bSHendrik Brueckner 
27944a01d5bSHendrik Brueckner 	case MSG_TYPE_WINSIZE:
28044a01d5bSHendrik Brueckner 		if (rb->mbuf->datalen != sizeof(struct winsize))
28144a01d5bSHendrik Brueckner 			break;
282254be490SHendrik Brueckner 		/* The caller must ensure that the hvc is locked, which
283254be490SHendrik Brueckner 		 * is the case when called from hvc_iucv_get_chars() */
284254be490SHendrik Brueckner 		__hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data));
28544a01d5bSHendrik Brueckner 		break;
28644a01d5bSHendrik Brueckner 
28744a01d5bSHendrik Brueckner 	case MSG_TYPE_ERROR:	/* ignored ... */
28844a01d5bSHendrik Brueckner 	case MSG_TYPE_TERMENV:	/* ignored ... */
28944a01d5bSHendrik Brueckner 	case MSG_TYPE_TERMIOS:	/* ignored ... */
29044a01d5bSHendrik Brueckner 		break;
29144a01d5bSHendrik Brueckner 	}
29244a01d5bSHendrik Brueckner 
29344a01d5bSHendrik Brueckner out_remove_buffer:
29444a01d5bSHendrik Brueckner 	list_del(&rb->list);
29544a01d5bSHendrik Brueckner 	destroy_tty_buffer(rb);
29644a01d5bSHendrik Brueckner 	*has_more_data = !list_empty(&priv->tty_inqueue);
29744a01d5bSHendrik Brueckner 
29844a01d5bSHendrik Brueckner out_written:
29944a01d5bSHendrik Brueckner 	return written;
30044a01d5bSHendrik Brueckner }
30144a01d5bSHendrik Brueckner 
30244a01d5bSHendrik Brueckner /**
30344a01d5bSHendrik Brueckner  * hvc_iucv_get_chars() - HVC get_chars operation.
30444a01d5bSHendrik Brueckner  * @vtermno:	HVC virtual terminal number.
30544a01d5bSHendrik Brueckner  * @buf:	Pointer to a buffer to store data
30644a01d5bSHendrik Brueckner  * @count:	Size of buffer available for writing
30744a01d5bSHendrik Brueckner  *
30817e19f04SHendrik Brueckner  * The HVC thread calls this method to read characters from the back-end.
30917e19f04SHendrik Brueckner  * If an IUCV communication path has been established, pending IUCV messages
31017e19f04SHendrik Brueckner  * are received and data is copied into buffer @buf up to @count bytes.
31144a01d5bSHendrik Brueckner  *
31244a01d5bSHendrik Brueckner  * Locking:	The routine gets called under an irqsave() spinlock; and
31344a01d5bSHendrik Brueckner  *		the routine locks the struct hvc_iucv_private->lock to call
31444a01d5bSHendrik Brueckner  *		helper functions.
31544a01d5bSHendrik Brueckner  */
31644a01d5bSHendrik Brueckner static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count)
31744a01d5bSHendrik Brueckner {
31844a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
31944a01d5bSHendrik Brueckner 	int written;
32044a01d5bSHendrik Brueckner 	int has_more_data;
32144a01d5bSHendrik Brueckner 
32244a01d5bSHendrik Brueckner 	if (count <= 0)
32344a01d5bSHendrik Brueckner 		return 0;
32444a01d5bSHendrik Brueckner 
32544a01d5bSHendrik Brueckner 	if (!priv)
32644a01d5bSHendrik Brueckner 		return -ENODEV;
32744a01d5bSHendrik Brueckner 
32844a01d5bSHendrik Brueckner 	spin_lock(&priv->lock);
32944a01d5bSHendrik Brueckner 	has_more_data = 0;
33044a01d5bSHendrik Brueckner 	written = hvc_iucv_write(priv, buf, count, &has_more_data);
33144a01d5bSHendrik Brueckner 	spin_unlock(&priv->lock);
33244a01d5bSHendrik Brueckner 
33344a01d5bSHendrik Brueckner 	/* if there are still messages on the queue... schedule another run */
33444a01d5bSHendrik Brueckner 	if (has_more_data)
33544a01d5bSHendrik Brueckner 		hvc_kick();
33644a01d5bSHendrik Brueckner 
33744a01d5bSHendrik Brueckner 	return written;
33844a01d5bSHendrik Brueckner }
33944a01d5bSHendrik Brueckner 
34044a01d5bSHendrik Brueckner /**
341c45ce4b5SHendrik Brueckner  * hvc_iucv_queue() - Buffer terminal data for sending.
34244a01d5bSHendrik Brueckner  * @priv:	Pointer to struct hvc_iucv_private instance.
34344a01d5bSHendrik Brueckner  * @buf:	Buffer containing data to send.
344c45ce4b5SHendrik Brueckner  * @count:	Size of buffer and amount of data to send.
34544a01d5bSHendrik Brueckner  *
346c45ce4b5SHendrik Brueckner  * The function queues data for sending. To actually send the buffered data,
34717e19f04SHendrik Brueckner  * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY).
348c45ce4b5SHendrik Brueckner  * The function returns the number of data bytes that has been buffered.
34944a01d5bSHendrik Brueckner  *
350c45ce4b5SHendrik Brueckner  * If the device is not connected, data is ignored and the function returns
351c45ce4b5SHendrik Brueckner  * @count.
352c45ce4b5SHendrik Brueckner  * If the buffer is full, the function returns 0.
35317e19f04SHendrik Brueckner  * If an existing IUCV communicaton path has been severed, -EPIPE is returned
35417e19f04SHendrik Brueckner  * (that can be passed to HVC layer to cause a tty hangup).
35544a01d5bSHendrik Brueckner  */
356c45ce4b5SHendrik Brueckner static int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf,
35744a01d5bSHendrik Brueckner 			  int count)
35844a01d5bSHendrik Brueckner {
359c45ce4b5SHendrik Brueckner 	size_t len;
360c45ce4b5SHendrik Brueckner 
361c45ce4b5SHendrik Brueckner 	if (priv->iucv_state == IUCV_DISCONN)
362c45ce4b5SHendrik Brueckner 		return count;			/* ignore data */
363c45ce4b5SHendrik Brueckner 
364c45ce4b5SHendrik Brueckner 	if (priv->iucv_state == IUCV_SEVERED)
365c45ce4b5SHendrik Brueckner 		return -EPIPE;
366c45ce4b5SHendrik Brueckner 
367c45ce4b5SHendrik Brueckner 	len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len);
368c45ce4b5SHendrik Brueckner 	if (!len)
369c45ce4b5SHendrik Brueckner 		return 0;
370c45ce4b5SHendrik Brueckner 
371c45ce4b5SHendrik Brueckner 	memcpy(priv->sndbuf + priv->sndbuf_len, buf, len);
372c45ce4b5SHendrik Brueckner 	priv->sndbuf_len += len;
373c45ce4b5SHendrik Brueckner 
374c45ce4b5SHendrik Brueckner 	if (priv->iucv_state == IUCV_CONNECTED)
375c45ce4b5SHendrik Brueckner 		schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY);
376c45ce4b5SHendrik Brueckner 
377c45ce4b5SHendrik Brueckner 	return len;
378c45ce4b5SHendrik Brueckner }
379c45ce4b5SHendrik Brueckner 
380c45ce4b5SHendrik Brueckner /**
381c45ce4b5SHendrik Brueckner  * hvc_iucv_send() - Send an IUCV message containing terminal data.
382c45ce4b5SHendrik Brueckner  * @priv:	Pointer to struct hvc_iucv_private instance.
383c45ce4b5SHendrik Brueckner  *
38417e19f04SHendrik Brueckner  * If an IUCV communication path has been established, the buffered output data
38517e19f04SHendrik Brueckner  * is sent via an IUCV message and the number of bytes sent is returned.
38617e19f04SHendrik Brueckner  * Returns 0 if there is no established IUCV communication path or
38717e19f04SHendrik Brueckner  * -EPIPE if an existing IUCV communicaton path has been severed.
388c45ce4b5SHendrik Brueckner  */
389c45ce4b5SHendrik Brueckner static int hvc_iucv_send(struct hvc_iucv_private *priv)
390c45ce4b5SHendrik Brueckner {
39144a01d5bSHendrik Brueckner 	struct iucv_tty_buffer *sb;
392c45ce4b5SHendrik Brueckner 	int rc, len;
39344a01d5bSHendrik Brueckner 
39444a01d5bSHendrik Brueckner 	if (priv->iucv_state == IUCV_SEVERED)
39544a01d5bSHendrik Brueckner 		return -EPIPE;
39644a01d5bSHendrik Brueckner 
39744a01d5bSHendrik Brueckner 	if (priv->iucv_state == IUCV_DISCONN)
398c45ce4b5SHendrik Brueckner 		return -EIO;
39944a01d5bSHendrik Brueckner 
400c45ce4b5SHendrik Brueckner 	if (!priv->sndbuf_len)
401c45ce4b5SHendrik Brueckner 		return 0;
40244a01d5bSHendrik Brueckner 
40344a01d5bSHendrik Brueckner 	/* allocate internal buffer to store msg data and also compute total
40444a01d5bSHendrik Brueckner 	 * message length */
405c45ce4b5SHendrik Brueckner 	sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC);
40644a01d5bSHendrik Brueckner 	if (!sb)
40744a01d5bSHendrik Brueckner 		return -ENOMEM;
40844a01d5bSHendrik Brueckner 
409c45ce4b5SHendrik Brueckner 	memcpy(sb->mbuf->data, priv->sndbuf, priv->sndbuf_len);
410c45ce4b5SHendrik Brueckner 	sb->mbuf->datalen = (u16) priv->sndbuf_len;
411c45ce4b5SHendrik Brueckner 	sb->msg.length = MSG_SIZE(sb->mbuf->datalen);
41244a01d5bSHendrik Brueckner 
41344a01d5bSHendrik Brueckner 	list_add_tail(&sb->list, &priv->tty_outqueue);
41444a01d5bSHendrik Brueckner 
41544a01d5bSHendrik Brueckner 	rc = __iucv_message_send(priv->path, &sb->msg, 0, 0,
41644a01d5bSHendrik Brueckner 				 (void *) sb->mbuf, sb->msg.length);
41744a01d5bSHendrik Brueckner 	if (rc) {
418c45ce4b5SHendrik Brueckner 		/* drop the message here; however we might want to handle
419c45ce4b5SHendrik Brueckner 		 * 0x03 (msg limit reached) by trying again... */
42044a01d5bSHendrik Brueckner 		list_del(&sb->list);
42144a01d5bSHendrik Brueckner 		destroy_tty_buffer(sb);
42244a01d5bSHendrik Brueckner 	}
423c45ce4b5SHendrik Brueckner 	len = priv->sndbuf_len;
424c45ce4b5SHendrik Brueckner 	priv->sndbuf_len = 0;
42544a01d5bSHendrik Brueckner 
42644a01d5bSHendrik Brueckner 	return len;
42744a01d5bSHendrik Brueckner }
42844a01d5bSHendrik Brueckner 
42944a01d5bSHendrik Brueckner /**
430c45ce4b5SHendrik Brueckner  * hvc_iucv_sndbuf_work() - Send buffered data over IUCV
431c45ce4b5SHendrik Brueckner  * @work:	Work structure.
432c45ce4b5SHendrik Brueckner  *
43317e19f04SHendrik Brueckner  * This work queue function sends buffered output data over IUCV and,
43417e19f04SHendrik Brueckner  * if not all buffered data could be sent, reschedules itself.
435c45ce4b5SHendrik Brueckner  */
436c45ce4b5SHendrik Brueckner static void hvc_iucv_sndbuf_work(struct work_struct *work)
437c45ce4b5SHendrik Brueckner {
438c45ce4b5SHendrik Brueckner 	struct hvc_iucv_private *priv;
439c45ce4b5SHendrik Brueckner 
440c45ce4b5SHendrik Brueckner 	priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work);
441c45ce4b5SHendrik Brueckner 	if (!priv)
442c45ce4b5SHendrik Brueckner 		return;
443c45ce4b5SHendrik Brueckner 
444c45ce4b5SHendrik Brueckner 	spin_lock_bh(&priv->lock);
445c45ce4b5SHendrik Brueckner 	hvc_iucv_send(priv);
446c45ce4b5SHendrik Brueckner 	spin_unlock_bh(&priv->lock);
447c45ce4b5SHendrik Brueckner }
448c45ce4b5SHendrik Brueckner 
449c45ce4b5SHendrik Brueckner /**
45044a01d5bSHendrik Brueckner  * hvc_iucv_put_chars() - HVC put_chars operation.
45144a01d5bSHendrik Brueckner  * @vtermno:	HVC virtual terminal number.
45244a01d5bSHendrik Brueckner  * @buf:	Pointer to an buffer to read data from
45344a01d5bSHendrik Brueckner  * @count:	Size of buffer available for reading
45444a01d5bSHendrik Brueckner  *
45517e19f04SHendrik Brueckner  * The HVC thread calls this method to write characters to the back-end.
45617e19f04SHendrik Brueckner  * The function calls hvc_iucv_queue() to queue terminal data for sending.
45744a01d5bSHendrik Brueckner  *
45844a01d5bSHendrik Brueckner  * Locking:	The method gets called under an irqsave() spinlock; and
45944a01d5bSHendrik Brueckner  *		locks struct hvc_iucv_private->lock.
46044a01d5bSHendrik Brueckner  */
46144a01d5bSHendrik Brueckner static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count)
46244a01d5bSHendrik Brueckner {
46344a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
464c45ce4b5SHendrik Brueckner 	int queued;
46544a01d5bSHendrik Brueckner 
46644a01d5bSHendrik Brueckner 	if (count <= 0)
46744a01d5bSHendrik Brueckner 		return 0;
46844a01d5bSHendrik Brueckner 
46944a01d5bSHendrik Brueckner 	if (!priv)
47044a01d5bSHendrik Brueckner 		return -ENODEV;
47144a01d5bSHendrik Brueckner 
47244a01d5bSHendrik Brueckner 	spin_lock(&priv->lock);
473c45ce4b5SHendrik Brueckner 	queued = hvc_iucv_queue(priv, buf, count);
47444a01d5bSHendrik Brueckner 	spin_unlock(&priv->lock);
47544a01d5bSHendrik Brueckner 
476c45ce4b5SHendrik Brueckner 	return queued;
47744a01d5bSHendrik Brueckner }
47844a01d5bSHendrik Brueckner 
47944a01d5bSHendrik Brueckner /**
48044a01d5bSHendrik Brueckner  * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time.
48144a01d5bSHendrik Brueckner  * @hp:	Pointer to the HVC device (struct hvc_struct)
48244a01d5bSHendrik Brueckner  * @id:	Additional data (originally passed to hvc_alloc): the index of an struct
48344a01d5bSHendrik Brueckner  *	hvc_iucv_private instance.
48444a01d5bSHendrik Brueckner  *
4856c089fd3SHendrik Brueckner  * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private
48644a01d5bSHendrik Brueckner  * instance that is derived from @id. Always returns 0.
48744a01d5bSHendrik Brueckner  *
48844a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock, spin_lock_bh
48944a01d5bSHendrik Brueckner  */
49044a01d5bSHendrik Brueckner static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id)
49144a01d5bSHendrik Brueckner {
49244a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv;
49344a01d5bSHendrik Brueckner 
49444a01d5bSHendrik Brueckner 	priv = hvc_iucv_get_private(id);
49544a01d5bSHendrik Brueckner 	if (!priv)
49644a01d5bSHendrik Brueckner 		return 0;
49744a01d5bSHendrik Brueckner 
49844a01d5bSHendrik Brueckner 	spin_lock_bh(&priv->lock);
49944a01d5bSHendrik Brueckner 	priv->tty_state = TTY_OPENED;
50044a01d5bSHendrik Brueckner 	spin_unlock_bh(&priv->lock);
50144a01d5bSHendrik Brueckner 
50244a01d5bSHendrik Brueckner 	return 0;
50344a01d5bSHendrik Brueckner }
50444a01d5bSHendrik Brueckner 
50544a01d5bSHendrik Brueckner /**
50617e19f04SHendrik Brueckner  * hvc_iucv_cleanup() - Clean up and reset a z/VM IUCV HVC instance.
50744a01d5bSHendrik Brueckner  * @priv:	Pointer to the struct hvc_iucv_private instance.
50844a01d5bSHendrik Brueckner  */
50944a01d5bSHendrik Brueckner static void hvc_iucv_cleanup(struct hvc_iucv_private *priv)
51044a01d5bSHendrik Brueckner {
51144a01d5bSHendrik Brueckner 	destroy_tty_buffer_list(&priv->tty_outqueue);
51244a01d5bSHendrik Brueckner 	destroy_tty_buffer_list(&priv->tty_inqueue);
51344a01d5bSHendrik Brueckner 
51444a01d5bSHendrik Brueckner 	priv->tty_state = TTY_CLOSED;
51544a01d5bSHendrik Brueckner 	priv->iucv_state = IUCV_DISCONN;
516c45ce4b5SHendrik Brueckner 
517c45ce4b5SHendrik Brueckner 	priv->sndbuf_len = 0;
518c45ce4b5SHendrik Brueckner }
519c45ce4b5SHendrik Brueckner 
520c45ce4b5SHendrik Brueckner /**
521c45ce4b5SHendrik Brueckner  * tty_outqueue_empty() - Test if the tty outq is empty
522c45ce4b5SHendrik Brueckner  * @priv:	Pointer to struct hvc_iucv_private instance.
523c45ce4b5SHendrik Brueckner  */
524c45ce4b5SHendrik Brueckner static inline int tty_outqueue_empty(struct hvc_iucv_private *priv)
525c45ce4b5SHendrik Brueckner {
526c45ce4b5SHendrik Brueckner 	int rc;
527c45ce4b5SHendrik Brueckner 
528c45ce4b5SHendrik Brueckner 	spin_lock_bh(&priv->lock);
529c45ce4b5SHendrik Brueckner 	rc = list_empty(&priv->tty_outqueue);
530c45ce4b5SHendrik Brueckner 	spin_unlock_bh(&priv->lock);
531c45ce4b5SHendrik Brueckner 
532c45ce4b5SHendrik Brueckner 	return rc;
533c45ce4b5SHendrik Brueckner }
534c45ce4b5SHendrik Brueckner 
535c45ce4b5SHendrik Brueckner /**
536c45ce4b5SHendrik Brueckner  * flush_sndbuf_sync() - Flush send buffer and wait for completion
537c45ce4b5SHendrik Brueckner  * @priv:	Pointer to struct hvc_iucv_private instance.
538c45ce4b5SHendrik Brueckner  *
539c45ce4b5SHendrik Brueckner  * The routine cancels a pending sndbuf work, calls hvc_iucv_send()
540c45ce4b5SHendrik Brueckner  * to flush any buffered terminal output data and waits for completion.
541c45ce4b5SHendrik Brueckner  */
542c45ce4b5SHendrik Brueckner static void flush_sndbuf_sync(struct hvc_iucv_private *priv)
543c45ce4b5SHendrik Brueckner {
544c45ce4b5SHendrik Brueckner 	int sync_wait;
545c45ce4b5SHendrik Brueckner 
546c45ce4b5SHendrik Brueckner 	cancel_delayed_work_sync(&priv->sndbuf_work);
547c45ce4b5SHendrik Brueckner 
548c45ce4b5SHendrik Brueckner 	spin_lock_bh(&priv->lock);
549c45ce4b5SHendrik Brueckner 	hvc_iucv_send(priv);		/* force sending buffered data */
550c45ce4b5SHendrik Brueckner 	sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */
551c45ce4b5SHendrik Brueckner 	spin_unlock_bh(&priv->lock);
552c45ce4b5SHendrik Brueckner 
553c45ce4b5SHendrik Brueckner 	if (sync_wait)
554c45ce4b5SHendrik Brueckner 		wait_event_timeout(priv->sndbuf_waitq,
5550259162eSHendrik Brueckner 				   tty_outqueue_empty(priv), HZ/10);
5560259162eSHendrik Brueckner }
5570259162eSHendrik Brueckner 
5580259162eSHendrik Brueckner /**
5590259162eSHendrik Brueckner  * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up
5600259162eSHendrik Brueckner  * @priv:	Pointer to hvc_iucv_private structure
5610259162eSHendrik Brueckner  *
5620259162eSHendrik Brueckner  * This routine severs an existing IUCV communication path and hangs
5630259162eSHendrik Brueckner  * up the underlying HVC terminal device.
5640259162eSHendrik Brueckner  * The hang-up occurs only if an IUCV communication path is established;
5650259162eSHendrik Brueckner  * otherwise there is no need to hang up the terminal device.
5660259162eSHendrik Brueckner  *
5670259162eSHendrik Brueckner  * The IUCV HVC hang-up is separated into two steps:
5680259162eSHendrik Brueckner  * 1. After the IUCV path has been severed, the iucv_state is set to
5690259162eSHendrik Brueckner  *    IUCV_SEVERED.
5700259162eSHendrik Brueckner  * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the
5710259162eSHendrik Brueckner  *    IUCV_SEVERED state causes the tty hang-up in the HVC layer.
5720259162eSHendrik Brueckner  *
5730259162eSHendrik Brueckner  * If the tty has not yet been opened, clean up the hvc_iucv_private
5740259162eSHendrik Brueckner  * structure to allow re-connects.
5750259162eSHendrik Brueckner  * If the tty has been opened, let get_chars() return -EPIPE to signal
5760259162eSHendrik Brueckner  * the HVC layer to hang up the tty and, if so, wake up the HVC thread
5770259162eSHendrik Brueckner  * to call get_chars()...
5780259162eSHendrik Brueckner  *
5790259162eSHendrik Brueckner  * Special notes on hanging up a HVC terminal instantiated as console:
5800259162eSHendrik Brueckner  * Hang-up:	1. do_tty_hangup() replaces file ops (= hung_up_tty_fops)
5810259162eSHendrik Brueckner  *		2. do_tty_hangup() calls tty->ops->close() for console_filp
5820259162eSHendrik Brueckner  *			=> no hangup notifier is called by HVC (default)
5830259162eSHendrik Brueckner  *		2. hvc_close() returns because of tty_hung_up_p(filp)
5840259162eSHendrik Brueckner  *			=> no delete notifier is called!
5850259162eSHendrik Brueckner  * Finally, the back-end is not being notified, thus, the tty session is
5860259162eSHendrik Brueckner  * kept active (TTY_OPEN) to be ready for re-connects.
5870259162eSHendrik Brueckner  *
5880259162eSHendrik Brueckner  * Locking:	spin_lock(&priv->lock) w/o disabling bh
5890259162eSHendrik Brueckner  */
5900259162eSHendrik Brueckner static void hvc_iucv_hangup(struct hvc_iucv_private *priv)
5910259162eSHendrik Brueckner {
5920259162eSHendrik Brueckner 	struct iucv_path *path;
5930259162eSHendrik Brueckner 
5940259162eSHendrik Brueckner 	path = NULL;
5950259162eSHendrik Brueckner 	spin_lock(&priv->lock);
5960259162eSHendrik Brueckner 	if (priv->iucv_state == IUCV_CONNECTED) {
5970259162eSHendrik Brueckner 		path = priv->path;
5980259162eSHendrik Brueckner 		priv->path = NULL;
5990259162eSHendrik Brueckner 		priv->iucv_state = IUCV_SEVERED;
6000259162eSHendrik Brueckner 		if (priv->tty_state == TTY_CLOSED)
6010259162eSHendrik Brueckner 			hvc_iucv_cleanup(priv);
6020259162eSHendrik Brueckner 		else
6030259162eSHendrik Brueckner 			/* console is special (see above) */
6040259162eSHendrik Brueckner 			if (priv->is_console) {
6050259162eSHendrik Brueckner 				hvc_iucv_cleanup(priv);
6060259162eSHendrik Brueckner 				priv->tty_state = TTY_OPENED;
6070259162eSHendrik Brueckner 			} else
6080259162eSHendrik Brueckner 				hvc_kick();
6090259162eSHendrik Brueckner 	}
6100259162eSHendrik Brueckner 	spin_unlock(&priv->lock);
6110259162eSHendrik Brueckner 
6120259162eSHendrik Brueckner 	/* finally sever path (outside of priv->lock due to lock ordering) */
6130259162eSHendrik Brueckner 	if (path) {
6140259162eSHendrik Brueckner 		iucv_path_sever(path, NULL);
6150259162eSHendrik Brueckner 		iucv_path_free(path);
6160259162eSHendrik Brueckner 	}
61744a01d5bSHendrik Brueckner }
61844a01d5bSHendrik Brueckner 
61944a01d5bSHendrik Brueckner /**
62017e19f04SHendrik Brueckner  * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups.
62144a01d5bSHendrik Brueckner  * @hp:		Pointer to the HVC device (struct hvc_struct)
62217e19f04SHendrik Brueckner  * @id:		Additional data (originally passed to hvc_alloc):
62317e19f04SHendrik Brueckner  *		the index of an struct hvc_iucv_private instance.
62444a01d5bSHendrik Brueckner  *
62517e19f04SHendrik Brueckner  * This routine notifies the HVC back-end that a tty hangup (carrier loss,
62625985edcSLucas De Marchi  * virtual or otherwise) has occurred.
62717e19f04SHendrik Brueckner  * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup())
62817e19f04SHendrik Brueckner  * to keep an existing IUCV communication path established.
62944a01d5bSHendrik Brueckner  * (Background: vhangup() is called from user space (by getty or login) to
63044a01d5bSHendrik Brueckner  *		disable writing to the tty by other applications).
63117e19f04SHendrik Brueckner  * If the tty has been opened and an established IUCV path has been severed
63217e19f04SHendrik Brueckner  * (we caused the tty hangup), the function calls hvc_iucv_cleanup().
63344a01d5bSHendrik Brueckner  *
63444a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock
63544a01d5bSHendrik Brueckner  */
63644a01d5bSHendrik Brueckner static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
63744a01d5bSHendrik Brueckner {
63844a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv;
63944a01d5bSHendrik Brueckner 
64044a01d5bSHendrik Brueckner 	priv = hvc_iucv_get_private(id);
64144a01d5bSHendrik Brueckner 	if (!priv)
64244a01d5bSHendrik Brueckner 		return;
64344a01d5bSHendrik Brueckner 
644c45ce4b5SHendrik Brueckner 	flush_sndbuf_sync(priv);
645c45ce4b5SHendrik Brueckner 
64644a01d5bSHendrik Brueckner 	spin_lock_bh(&priv->lock);
64744a01d5bSHendrik Brueckner 	/* NOTE: If the hangup was scheduled by ourself (from the iucv
64817e19f04SHendrik Brueckner 	 *	 path_servered callback [IUCV_SEVERED]), we have to clean up
64917e19f04SHendrik Brueckner 	 *	 our structure and to set state to TTY_CLOSED.
65044a01d5bSHendrik Brueckner 	 *	 If the tty was hung up otherwise (e.g. vhangup()), then we
65144a01d5bSHendrik Brueckner 	 *	 ignore this hangup and keep an established IUCV path open...
65244a01d5bSHendrik Brueckner 	 *	 (...the reason is that we are not able to connect back to the
65344a01d5bSHendrik Brueckner 	 *	 client if we disconnect on hang up) */
65444a01d5bSHendrik Brueckner 	priv->tty_state = TTY_CLOSED;
65544a01d5bSHendrik Brueckner 
65644a01d5bSHendrik Brueckner 	if (priv->iucv_state == IUCV_SEVERED)
65744a01d5bSHendrik Brueckner 		hvc_iucv_cleanup(priv);
65844a01d5bSHendrik Brueckner 	spin_unlock_bh(&priv->lock);
65944a01d5bSHendrik Brueckner }
66044a01d5bSHendrik Brueckner 
66144a01d5bSHendrik Brueckner /**
66274b3b4cdSHendrik Brueckner  * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS
66374b3b4cdSHendrik Brueckner  * @hp:		Pointer the HVC device (struct hvc_struct)
66474b3b4cdSHendrik Brueckner  * @raise:	Non-zero to raise or zero to lower DTR/RTS lines
66574b3b4cdSHendrik Brueckner  *
66674b3b4cdSHendrik Brueckner  * This routine notifies the HVC back-end to raise or lower DTR/RTS
66774b3b4cdSHendrik Brueckner  * lines.  Raising DTR/RTS is ignored.  Lowering DTR/RTS indicates to
66874b3b4cdSHendrik Brueckner  * drop the IUCV connection (similar to hang up the modem).
66974b3b4cdSHendrik Brueckner  */
67074b3b4cdSHendrik Brueckner static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise)
67174b3b4cdSHendrik Brueckner {
67274b3b4cdSHendrik Brueckner 	struct hvc_iucv_private *priv;
67374b3b4cdSHendrik Brueckner 	struct iucv_path        *path;
67474b3b4cdSHendrik Brueckner 
67574b3b4cdSHendrik Brueckner 	/* Raising the DTR/RTS is ignored as IUCV connections can be
67674b3b4cdSHendrik Brueckner 	 * established at any times.
67774b3b4cdSHendrik Brueckner 	 */
67874b3b4cdSHendrik Brueckner 	if (raise)
67974b3b4cdSHendrik Brueckner 		return;
68074b3b4cdSHendrik Brueckner 
68174b3b4cdSHendrik Brueckner 	priv = hvc_iucv_get_private(hp->vtermno);
68274b3b4cdSHendrik Brueckner 	if (!priv)
68374b3b4cdSHendrik Brueckner 		return;
68474b3b4cdSHendrik Brueckner 
68574b3b4cdSHendrik Brueckner 	/* Lowering the DTR/RTS lines disconnects an established IUCV
68674b3b4cdSHendrik Brueckner 	 * connection.
68774b3b4cdSHendrik Brueckner 	 */
68874b3b4cdSHendrik Brueckner 	flush_sndbuf_sync(priv);
68974b3b4cdSHendrik Brueckner 
69074b3b4cdSHendrik Brueckner 	spin_lock_bh(&priv->lock);
69174b3b4cdSHendrik Brueckner 	path = priv->path;		/* save reference to IUCV path */
69274b3b4cdSHendrik Brueckner 	priv->path = NULL;
69374b3b4cdSHendrik Brueckner 	priv->iucv_state = IUCV_DISCONN;
69474b3b4cdSHendrik Brueckner 	spin_unlock_bh(&priv->lock);
69574b3b4cdSHendrik Brueckner 
69674b3b4cdSHendrik Brueckner 	/* Sever IUCV path outside of priv->lock due to lock ordering of:
69774b3b4cdSHendrik Brueckner 	 * priv->lock <--> iucv_table_lock */
69874b3b4cdSHendrik Brueckner 	if (path) {
69974b3b4cdSHendrik Brueckner 		iucv_path_sever(path, NULL);
70074b3b4cdSHendrik Brueckner 		iucv_path_free(path);
70174b3b4cdSHendrik Brueckner 	}
70274b3b4cdSHendrik Brueckner }
70374b3b4cdSHendrik Brueckner 
70474b3b4cdSHendrik Brueckner /**
70544a01d5bSHendrik Brueckner  * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
70644a01d5bSHendrik Brueckner  * @hp:		Pointer to the HVC device (struct hvc_struct)
70744a01d5bSHendrik Brueckner  * @id:		Additional data (originally passed to hvc_alloc):
70844a01d5bSHendrik Brueckner  *		the index of an struct hvc_iucv_private instance.
70944a01d5bSHendrik Brueckner  *
71017e19f04SHendrik Brueckner  * This routine notifies the HVC back-end that the last tty device fd has been
71174b3b4cdSHendrik Brueckner  * closed.  The function cleans up tty resources.  The clean-up of the IUCV
71274b3b4cdSHendrik Brueckner  * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios
71374b3b4cdSHendrik Brueckner  * control setting.
71444a01d5bSHendrik Brueckner  *
71544a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock
71644a01d5bSHendrik Brueckner  */
71744a01d5bSHendrik Brueckner static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
71844a01d5bSHendrik Brueckner {
71944a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv;
72044a01d5bSHendrik Brueckner 
72144a01d5bSHendrik Brueckner 	priv = hvc_iucv_get_private(id);
72244a01d5bSHendrik Brueckner 	if (!priv)
72344a01d5bSHendrik Brueckner 		return;
72444a01d5bSHendrik Brueckner 
725c45ce4b5SHendrik Brueckner 	flush_sndbuf_sync(priv);
726c45ce4b5SHendrik Brueckner 
72744a01d5bSHendrik Brueckner 	spin_lock_bh(&priv->lock);
72874b3b4cdSHendrik Brueckner 	destroy_tty_buffer_list(&priv->tty_outqueue);
72974b3b4cdSHendrik Brueckner 	destroy_tty_buffer_list(&priv->tty_inqueue);
73074b3b4cdSHendrik Brueckner 	priv->tty_state = TTY_CLOSED;
73174b3b4cdSHendrik Brueckner 	priv->sndbuf_len = 0;
73244a01d5bSHendrik Brueckner 	spin_unlock_bh(&priv->lock);
73344a01d5bSHendrik Brueckner }
73444a01d5bSHendrik Brueckner 
73544a01d5bSHendrik Brueckner /**
736431429ffSHendrik Brueckner  * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID
737431429ffSHendrik Brueckner  * @ipvmid:	Originating z/VM user ID (right padded with blanks)
738431429ffSHendrik Brueckner  *
739926a7336SHendrik Brueckner  * Returns 0 if the z/VM user ID that is specified with @ipvmid is permitted to
740926a7336SHendrik Brueckner  * connect, otherwise non-zero.
741431429ffSHendrik Brueckner  */
742431429ffSHendrik Brueckner static int hvc_iucv_filter_connreq(u8 ipvmid[8])
743431429ffSHendrik Brueckner {
744926a7336SHendrik Brueckner 	const char *wildcard, *filter_entry;
745926a7336SHendrik Brueckner 	size_t i, len;
746431429ffSHendrik Brueckner 
747431429ffSHendrik Brueckner 	/* Note: default policy is ACCEPT if no filter is set */
748431429ffSHendrik Brueckner 	if (!hvc_iucv_filter_size)
749431429ffSHendrik Brueckner 		return 0;
750431429ffSHendrik Brueckner 
751926a7336SHendrik Brueckner 	for (i = 0; i < hvc_iucv_filter_size; i++) {
752926a7336SHendrik Brueckner 		filter_entry = hvc_iucv_filter + (8 * i);
753926a7336SHendrik Brueckner 
754926a7336SHendrik Brueckner 		/* If a filter entry contains the filter wildcard character,
755926a7336SHendrik Brueckner 		 * reduce the length to match the leading portion of the user
756926a7336SHendrik Brueckner 		 * ID only (wildcard match).  Characters following the wildcard
757926a7336SHendrik Brueckner 		 * are ignored.
758926a7336SHendrik Brueckner 		 */
759926a7336SHendrik Brueckner 		wildcard = strnchr(filter_entry, 8, FILTER_WILDCARD_CHAR);
760926a7336SHendrik Brueckner 		len = (wildcard) ? wildcard - filter_entry : 8;
761926a7336SHendrik Brueckner 		if (0 == memcmp(ipvmid, filter_entry, len))
762431429ffSHendrik Brueckner 			return 0;
763926a7336SHendrik Brueckner 	}
764431429ffSHendrik Brueckner 	return 1;
765431429ffSHendrik Brueckner }
766431429ffSHendrik Brueckner 
767431429ffSHendrik Brueckner /**
76844a01d5bSHendrik Brueckner  * hvc_iucv_path_pending() - IUCV handler to process a connection request.
76944a01d5bSHendrik Brueckner  * @path:	Pending path (struct iucv_path)
77017e19f04SHendrik Brueckner  * @ipvmid:	z/VM system identifier of originator
77144a01d5bSHendrik Brueckner  * @ipuser:	User specified data for this path
77244a01d5bSHendrik Brueckner  *		(AF_IUCV: port/service name and originator port)
77344a01d5bSHendrik Brueckner  *
77417e19f04SHendrik Brueckner  * The function uses the @ipuser data to determine if the pending path belongs
77517e19f04SHendrik Brueckner  * to a terminal managed by this device driver.
77617e19f04SHendrik Brueckner  * If the path belongs to this driver, ensure that the terminal is not accessed
77717e19f04SHendrik Brueckner  * multiple times (only one connection to a terminal is allowed).
77817e19f04SHendrik Brueckner  * If the terminal is not yet connected, the pending path is accepted and is
77917e19f04SHendrik Brueckner  * associated to the appropriate struct hvc_iucv_private instance.
78044a01d5bSHendrik Brueckner  *
78117e19f04SHendrik Brueckner  * Returns 0 if @path belongs to a terminal managed by the this device driver;
78244a01d5bSHendrik Brueckner  * otherwise returns -ENODEV in order to dispatch this path to other handlers.
78344a01d5bSHendrik Brueckner  *
78444a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock
78544a01d5bSHendrik Brueckner  */
78691e60eb6SUrsula Braun static	int hvc_iucv_path_pending(struct iucv_path *path, u8 *ipvmid,
78791e60eb6SUrsula Braun 				  u8 *ipuser)
78844a01d5bSHendrik Brueckner {
7894f592219SHendrik Brueckner 	struct hvc_iucv_private *priv, *tmp;
7904f592219SHendrik Brueckner 	u8 wildcard[9] = "lnxhvc  ";
7914f592219SHendrik Brueckner 	int i, rc, find_unused;
79244a01d5bSHendrik Brueckner 	u8 nuser_data[16];
793431429ffSHendrik Brueckner 	u8 vm_user_id[9];
79444a01d5bSHendrik Brueckner 
7954f592219SHendrik Brueckner 	ASCEBC(wildcard, sizeof(wildcard));
7964f592219SHendrik Brueckner 	find_unused = !memcmp(wildcard, ipuser, 8);
7974f592219SHendrik Brueckner 
7984f592219SHendrik Brueckner 	/* First, check if the pending path request is managed by this
7994f592219SHendrik Brueckner 	 * IUCV handler:
8004f592219SHendrik Brueckner 	 * - find a disconnected device if ipuser contains the wildcard
8014f592219SHendrik Brueckner 	 * - find the device that matches the terminal ID in ipuser
8024f592219SHendrik Brueckner 	 */
80344a01d5bSHendrik Brueckner 	priv = NULL;
8044f592219SHendrik Brueckner 	for (i = 0; i < hvc_iucv_devices; i++) {
8054f592219SHendrik Brueckner 		tmp = hvc_iucv_table[i];
8064f592219SHendrik Brueckner 		if (!tmp)
8074f592219SHendrik Brueckner 			continue;
8084f592219SHendrik Brueckner 
8094f592219SHendrik Brueckner 		if (find_unused) {
8104f592219SHendrik Brueckner 			spin_lock(&tmp->lock);
8114f592219SHendrik Brueckner 			if (tmp->iucv_state == IUCV_DISCONN)
8124f592219SHendrik Brueckner 				priv = tmp;
8134f592219SHendrik Brueckner 			spin_unlock(&tmp->lock);
8144f592219SHendrik Brueckner 
8154f592219SHendrik Brueckner 		} else if (!memcmp(tmp->srv_name, ipuser, 8))
8164f592219SHendrik Brueckner 				priv = tmp;
8174f592219SHendrik Brueckner 		if (priv)
81844a01d5bSHendrik Brueckner 			break;
81944a01d5bSHendrik Brueckner 	}
82044a01d5bSHendrik Brueckner 	if (!priv)
82144a01d5bSHendrik Brueckner 		return -ENODEV;
82244a01d5bSHendrik Brueckner 
823431429ffSHendrik Brueckner 	/* Enforce that ipvmid is allowed to connect to us */
824431429ffSHendrik Brueckner 	read_lock(&hvc_iucv_filter_lock);
825431429ffSHendrik Brueckner 	rc = hvc_iucv_filter_connreq(ipvmid);
826431429ffSHendrik Brueckner 	read_unlock(&hvc_iucv_filter_lock);
827431429ffSHendrik Brueckner 	if (rc) {
828431429ffSHendrik Brueckner 		iucv_path_sever(path, ipuser);
829431429ffSHendrik Brueckner 		iucv_path_free(path);
830431429ffSHendrik Brueckner 		memcpy(vm_user_id, ipvmid, 8);
831431429ffSHendrik Brueckner 		vm_user_id[8] = 0;
832431429ffSHendrik Brueckner 		pr_info("A connection request from z/VM user ID %s "
833431429ffSHendrik Brueckner 			"was refused\n", vm_user_id);
834431429ffSHendrik Brueckner 		return 0;
835431429ffSHendrik Brueckner 	}
836431429ffSHendrik Brueckner 
83744a01d5bSHendrik Brueckner 	spin_lock(&priv->lock);
83844a01d5bSHendrik Brueckner 
83944a01d5bSHendrik Brueckner 	/* If the terminal is already connected or being severed, then sever
84044a01d5bSHendrik Brueckner 	 * this path to enforce that there is only ONE established communication
84144a01d5bSHendrik Brueckner 	 * path per terminal. */
84244a01d5bSHendrik Brueckner 	if (priv->iucv_state != IUCV_DISCONN) {
84344a01d5bSHendrik Brueckner 		iucv_path_sever(path, ipuser);
84444a01d5bSHendrik Brueckner 		iucv_path_free(path);
84544a01d5bSHendrik Brueckner 		goto out_path_handled;
84644a01d5bSHendrik Brueckner 	}
84744a01d5bSHendrik Brueckner 
84844a01d5bSHendrik Brueckner 	/* accept path */
84944a01d5bSHendrik Brueckner 	memcpy(nuser_data, ipuser + 8, 8);  /* remote service (for af_iucv) */
85044a01d5bSHendrik Brueckner 	memcpy(nuser_data + 8, ipuser, 8);  /* local service  (for af_iucv) */
85144a01d5bSHendrik Brueckner 	path->msglim = 0xffff;		    /* IUCV MSGLIMIT */
85244a01d5bSHendrik Brueckner 	path->flags &= ~IUCV_IPRMDATA;	    /* TODO: use IUCV_IPRMDATA */
85344a01d5bSHendrik Brueckner 	rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv);
85444a01d5bSHendrik Brueckner 	if (rc) {
85544a01d5bSHendrik Brueckner 		iucv_path_sever(path, ipuser);
85644a01d5bSHendrik Brueckner 		iucv_path_free(path);
85744a01d5bSHendrik Brueckner 		goto out_path_handled;
85844a01d5bSHendrik Brueckner 	}
85944a01d5bSHendrik Brueckner 	priv->path = path;
86044a01d5bSHendrik Brueckner 	priv->iucv_state = IUCV_CONNECTED;
86144a01d5bSHendrik Brueckner 
862f1206badSHendrik Brueckner 	/* store path information */
863f1206badSHendrik Brueckner 	memcpy(priv->info_path, ipvmid, 8);
864f1206badSHendrik Brueckner 	memcpy(priv->info_path + 8, ipuser + 8, 8);
865f1206badSHendrik Brueckner 
866c45ce4b5SHendrik Brueckner 	/* flush buffered output data... */
867c45ce4b5SHendrik Brueckner 	schedule_delayed_work(&priv->sndbuf_work, 5);
868c45ce4b5SHendrik Brueckner 
86944a01d5bSHendrik Brueckner out_path_handled:
87044a01d5bSHendrik Brueckner 	spin_unlock(&priv->lock);
87144a01d5bSHendrik Brueckner 	return 0;
87244a01d5bSHendrik Brueckner }
87344a01d5bSHendrik Brueckner 
87444a01d5bSHendrik Brueckner /**
87544a01d5bSHendrik Brueckner  * hvc_iucv_path_severed() - IUCV handler to process a path sever.
87644a01d5bSHendrik Brueckner  * @path:	Pending path (struct iucv_path)
87744a01d5bSHendrik Brueckner  * @ipuser:	User specified data for this path
87844a01d5bSHendrik Brueckner  *		(AF_IUCV: port/service name and originator port)
87944a01d5bSHendrik Brueckner  *
8800259162eSHendrik Brueckner  * This function calls the hvc_iucv_hangup() function for the
8810259162eSHendrik Brueckner  * respective IUCV HVC terminal.
88244a01d5bSHendrik Brueckner  *
88344a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock
88444a01d5bSHendrik Brueckner  */
88591e60eb6SUrsula Braun static void hvc_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
88644a01d5bSHendrik Brueckner {
88744a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv = path->private;
88844a01d5bSHendrik Brueckner 
8890259162eSHendrik Brueckner 	hvc_iucv_hangup(priv);
89044a01d5bSHendrik Brueckner }
89144a01d5bSHendrik Brueckner 
89244a01d5bSHendrik Brueckner /**
89344a01d5bSHendrik Brueckner  * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message.
89444a01d5bSHendrik Brueckner  * @path:	Pending path (struct iucv_path)
89544a01d5bSHendrik Brueckner  * @msg:	Pointer to the IUCV message
89644a01d5bSHendrik Brueckner  *
89717e19f04SHendrik Brueckner  * The function puts an incoming message on the input queue for later
89844a01d5bSHendrik Brueckner  * processing (by hvc_iucv_get_chars() / hvc_iucv_write()).
89917e19f04SHendrik Brueckner  * If the tty has not yet been opened, the message is rejected.
90044a01d5bSHendrik Brueckner  *
90144a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock
90244a01d5bSHendrik Brueckner  */
90344a01d5bSHendrik Brueckner static void hvc_iucv_msg_pending(struct iucv_path *path,
90444a01d5bSHendrik Brueckner 				 struct iucv_message *msg)
90544a01d5bSHendrik Brueckner {
90644a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv = path->private;
90744a01d5bSHendrik Brueckner 	struct iucv_tty_buffer *rb;
90844a01d5bSHendrik Brueckner 
909c45ce4b5SHendrik Brueckner 	/* reject messages that exceed max size of iucv_tty_msg->datalen */
910c45ce4b5SHendrik Brueckner 	if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) {
911c45ce4b5SHendrik Brueckner 		iucv_message_reject(path, msg);
912c45ce4b5SHendrik Brueckner 		return;
913c45ce4b5SHendrik Brueckner 	}
914c45ce4b5SHendrik Brueckner 
91544a01d5bSHendrik Brueckner 	spin_lock(&priv->lock);
91644a01d5bSHendrik Brueckner 
91744a01d5bSHendrik Brueckner 	/* reject messages if tty has not yet been opened */
91844a01d5bSHendrik Brueckner 	if (priv->tty_state == TTY_CLOSED) {
91944a01d5bSHendrik Brueckner 		iucv_message_reject(path, msg);
92044a01d5bSHendrik Brueckner 		goto unlock_return;
92144a01d5bSHendrik Brueckner 	}
92244a01d5bSHendrik Brueckner 
923c45ce4b5SHendrik Brueckner 	/* allocate tty buffer to save iucv msg only */
92444a01d5bSHendrik Brueckner 	rb = alloc_tty_buffer(0, GFP_ATOMIC);
92544a01d5bSHendrik Brueckner 	if (!rb) {
92644a01d5bSHendrik Brueckner 		iucv_message_reject(path, msg);
92744a01d5bSHendrik Brueckner 		goto unlock_return;	/* -ENOMEM */
92844a01d5bSHendrik Brueckner 	}
92944a01d5bSHendrik Brueckner 	rb->msg = *msg;
93044a01d5bSHendrik Brueckner 
93144a01d5bSHendrik Brueckner 	list_add_tail(&rb->list, &priv->tty_inqueue);
93244a01d5bSHendrik Brueckner 
93317e19f04SHendrik Brueckner 	hvc_kick();	/* wake up hvc thread */
93444a01d5bSHendrik Brueckner 
93544a01d5bSHendrik Brueckner unlock_return:
93644a01d5bSHendrik Brueckner 	spin_unlock(&priv->lock);
93744a01d5bSHendrik Brueckner }
93844a01d5bSHendrik Brueckner 
93944a01d5bSHendrik Brueckner /**
94044a01d5bSHendrik Brueckner  * hvc_iucv_msg_complete() - IUCV handler to process message completion
94144a01d5bSHendrik Brueckner  * @path:	Pending path (struct iucv_path)
94244a01d5bSHendrik Brueckner  * @msg:	Pointer to the IUCV message
94344a01d5bSHendrik Brueckner  *
94417e19f04SHendrik Brueckner  * The function is called upon completion of message delivery to remove the
94517e19f04SHendrik Brueckner  * message from the outqueue. Additional delivery information can be found
94617e19f04SHendrik Brueckner  * msg->audit: rejected messages (0x040000 (IPADRJCT)), and
94744a01d5bSHendrik Brueckner  *	       purged messages	 (0x010000 (IPADPGNR)).
94844a01d5bSHendrik Brueckner  *
94944a01d5bSHendrik Brueckner  * Locking:	struct hvc_iucv_private->lock
95044a01d5bSHendrik Brueckner  */
95144a01d5bSHendrik Brueckner static void hvc_iucv_msg_complete(struct iucv_path *path,
95244a01d5bSHendrik Brueckner 				  struct iucv_message *msg)
95344a01d5bSHendrik Brueckner {
95444a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv = path->private;
95544a01d5bSHendrik Brueckner 	struct iucv_tty_buffer	*ent, *next;
95644a01d5bSHendrik Brueckner 	LIST_HEAD(list_remove);
95744a01d5bSHendrik Brueckner 
95844a01d5bSHendrik Brueckner 	spin_lock(&priv->lock);
95944a01d5bSHendrik Brueckner 	list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list)
96044a01d5bSHendrik Brueckner 		if (ent->msg.id == msg->id) {
96144a01d5bSHendrik Brueckner 			list_move(&ent->list, &list_remove);
96244a01d5bSHendrik Brueckner 			break;
96344a01d5bSHendrik Brueckner 		}
964c45ce4b5SHendrik Brueckner 	wake_up(&priv->sndbuf_waitq);
96544a01d5bSHendrik Brueckner 	spin_unlock(&priv->lock);
96644a01d5bSHendrik Brueckner 	destroy_tty_buffer_list(&list_remove);
96744a01d5bSHendrik Brueckner }
96844a01d5bSHendrik Brueckner 
9690259162eSHendrik Brueckner /**
9700259162eSHendrik Brueckner  * hvc_iucv_pm_freeze() - Freeze PM callback
9710259162eSHendrik Brueckner  * @dev:	IUVC HVC terminal device
9720259162eSHendrik Brueckner  *
9730259162eSHendrik Brueckner  * Sever an established IUCV communication path and
9740259162eSHendrik Brueckner  * trigger a hang-up of the underlying HVC terminal.
9750259162eSHendrik Brueckner  */
9760259162eSHendrik Brueckner static int hvc_iucv_pm_freeze(struct device *dev)
9770259162eSHendrik Brueckner {
9780259162eSHendrik Brueckner 	struct hvc_iucv_private *priv = dev_get_drvdata(dev);
9790259162eSHendrik Brueckner 
9800259162eSHendrik Brueckner 	local_bh_disable();
9810259162eSHendrik Brueckner 	hvc_iucv_hangup(priv);
9820259162eSHendrik Brueckner 	local_bh_enable();
9830259162eSHendrik Brueckner 
9840259162eSHendrik Brueckner 	return 0;
9850259162eSHendrik Brueckner }
9860259162eSHendrik Brueckner 
9870259162eSHendrik Brueckner /**
9880259162eSHendrik Brueckner  * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback
9890259162eSHendrik Brueckner  * @dev:	IUVC HVC terminal device
9900259162eSHendrik Brueckner  *
9910259162eSHendrik Brueckner  * Wake up the HVC thread to trigger hang-up and respective
9920259162eSHendrik Brueckner  * HVC back-end notifier invocations.
9930259162eSHendrik Brueckner  */
9940259162eSHendrik Brueckner static int hvc_iucv_pm_restore_thaw(struct device *dev)
9950259162eSHendrik Brueckner {
9960259162eSHendrik Brueckner 	hvc_kick();
9970259162eSHendrik Brueckner 	return 0;
9980259162eSHendrik Brueckner }
9990259162eSHendrik Brueckner 
1000f1206badSHendrik Brueckner static ssize_t hvc_iucv_dev_termid_show(struct device *dev,
1001f1206badSHendrik Brueckner 					struct device_attribute *attr,
1002f1206badSHendrik Brueckner 					char *buf)
1003f1206badSHendrik Brueckner {
1004f1206badSHendrik Brueckner 	struct hvc_iucv_private *priv = dev_get_drvdata(dev);
1005f1206badSHendrik Brueckner 	size_t len;
1006f1206badSHendrik Brueckner 
1007f1206badSHendrik Brueckner 	len = sizeof(priv->srv_name);
1008f1206badSHendrik Brueckner 	memcpy(buf, priv->srv_name, len);
1009f1206badSHendrik Brueckner 	EBCASC(buf, len);
1010f1206badSHendrik Brueckner 	buf[len++] = '\n';
1011f1206badSHendrik Brueckner 	return len;
1012f1206badSHendrik Brueckner }
1013f1206badSHendrik Brueckner 
1014f1206badSHendrik Brueckner static ssize_t hvc_iucv_dev_state_show(struct device *dev,
1015f1206badSHendrik Brueckner 					struct device_attribute *attr,
1016f1206badSHendrik Brueckner 					char *buf)
1017f1206badSHendrik Brueckner {
1018f1206badSHendrik Brueckner 	struct hvc_iucv_private *priv = dev_get_drvdata(dev);
1019f1206badSHendrik Brueckner 	return sprintf(buf, "%u:%u\n", priv->iucv_state, priv->tty_state);
1020f1206badSHendrik Brueckner }
1021f1206badSHendrik Brueckner 
1022f1206badSHendrik Brueckner static ssize_t hvc_iucv_dev_peer_show(struct device *dev,
1023f1206badSHendrik Brueckner 				      struct device_attribute *attr,
1024f1206badSHendrik Brueckner 				      char *buf)
1025f1206badSHendrik Brueckner {
1026f1206badSHendrik Brueckner 	struct hvc_iucv_private *priv = dev_get_drvdata(dev);
1027f1206badSHendrik Brueckner 	char vmid[9], ipuser[9];
1028f1206badSHendrik Brueckner 
1029f1206badSHendrik Brueckner 	memset(vmid, 0, sizeof(vmid));
1030f1206badSHendrik Brueckner 	memset(ipuser, 0, sizeof(ipuser));
1031f1206badSHendrik Brueckner 
1032f1206badSHendrik Brueckner 	spin_lock_bh(&priv->lock);
1033f1206badSHendrik Brueckner 	if (priv->iucv_state == IUCV_CONNECTED) {
1034f1206badSHendrik Brueckner 		memcpy(vmid, priv->info_path, 8);
1035f1206badSHendrik Brueckner 		memcpy(ipuser, priv->info_path + 8, 8);
1036f1206badSHendrik Brueckner 	}
1037f1206badSHendrik Brueckner 	spin_unlock_bh(&priv->lock);
1038f1206badSHendrik Brueckner 	EBCASC(ipuser, 8);
1039f1206badSHendrik Brueckner 
1040f1206badSHendrik Brueckner 	return sprintf(buf, "%s:%s\n", vmid, ipuser);
1041f1206badSHendrik Brueckner }
1042f1206badSHendrik Brueckner 
104344a01d5bSHendrik Brueckner 
104444a01d5bSHendrik Brueckner /* HVC operations */
10451dff3996SRusty Russell static const struct hv_ops hvc_iucv_ops = {
104644a01d5bSHendrik Brueckner 	.get_chars = hvc_iucv_get_chars,
104744a01d5bSHendrik Brueckner 	.put_chars = hvc_iucv_put_chars,
104844a01d5bSHendrik Brueckner 	.notifier_add = hvc_iucv_notifier_add,
104944a01d5bSHendrik Brueckner 	.notifier_del = hvc_iucv_notifier_del,
105044a01d5bSHendrik Brueckner 	.notifier_hangup = hvc_iucv_notifier_hangup,
105174b3b4cdSHendrik Brueckner 	.dtr_rts = hvc_iucv_dtr_rts,
105244a01d5bSHendrik Brueckner };
105344a01d5bSHendrik Brueckner 
10540259162eSHendrik Brueckner /* Suspend / resume device operations */
105547145210SAlexey Dobriyan static const struct dev_pm_ops hvc_iucv_pm_ops = {
10560259162eSHendrik Brueckner 	.freeze	  = hvc_iucv_pm_freeze,
10570259162eSHendrik Brueckner 	.thaw	  = hvc_iucv_pm_restore_thaw,
10580259162eSHendrik Brueckner 	.restore  = hvc_iucv_pm_restore_thaw,
10590259162eSHendrik Brueckner };
10600259162eSHendrik Brueckner 
10610259162eSHendrik Brueckner /* IUCV HVC device driver */
10620259162eSHendrik Brueckner static struct device_driver hvc_iucv_driver = {
10630259162eSHendrik Brueckner 	.name = KMSG_COMPONENT,
10640259162eSHendrik Brueckner 	.bus  = &iucv_bus,
10650259162eSHendrik Brueckner 	.pm   = &hvc_iucv_pm_ops,
10660259162eSHendrik Brueckner };
10670259162eSHendrik Brueckner 
1068f1206badSHendrik Brueckner /* IUCV HVC device attributes */
1069f1206badSHendrik Brueckner static DEVICE_ATTR(termid, 0640, hvc_iucv_dev_termid_show, NULL);
1070f1206badSHendrik Brueckner static DEVICE_ATTR(state, 0640, hvc_iucv_dev_state_show, NULL);
1071f1206badSHendrik Brueckner static DEVICE_ATTR(peer, 0640, hvc_iucv_dev_peer_show, NULL);
1072f1206badSHendrik Brueckner static struct attribute *hvc_iucv_dev_attrs[] = {
1073f1206badSHendrik Brueckner 	&dev_attr_termid.attr,
1074f1206badSHendrik Brueckner 	&dev_attr_state.attr,
1075f1206badSHendrik Brueckner 	&dev_attr_peer.attr,
1076f1206badSHendrik Brueckner 	NULL,
1077f1206badSHendrik Brueckner };
1078f1206badSHendrik Brueckner static struct attribute_group hvc_iucv_dev_attr_group = {
1079f1206badSHendrik Brueckner 	.attrs = hvc_iucv_dev_attrs,
1080f1206badSHendrik Brueckner };
1081f1206badSHendrik Brueckner static const struct attribute_group *hvc_iucv_dev_attr_groups[] = {
1082f1206badSHendrik Brueckner 	&hvc_iucv_dev_attr_group,
1083f1206badSHendrik Brueckner 	NULL,
1084f1206badSHendrik Brueckner };
1085f1206badSHendrik Brueckner 
1086f1206badSHendrik Brueckner 
108744a01d5bSHendrik Brueckner /**
108844a01d5bSHendrik Brueckner  * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance
108944a01d5bSHendrik Brueckner  * @id:			hvc_iucv_table index
10906c089fd3SHendrik Brueckner  * @is_console:		Flag if the instance is used as Linux console
109144a01d5bSHendrik Brueckner  *
109217e19f04SHendrik Brueckner  * This function allocates a new hvc_iucv_private structure and stores
109317e19f04SHendrik Brueckner  * the instance in hvc_iucv_table at index @id.
109444a01d5bSHendrik Brueckner  * Returns 0 on success; otherwise non-zero.
109544a01d5bSHendrik Brueckner  */
10966c089fd3SHendrik Brueckner static int __init hvc_iucv_alloc(int id, unsigned int is_console)
109744a01d5bSHendrik Brueckner {
109844a01d5bSHendrik Brueckner 	struct hvc_iucv_private *priv;
109944a01d5bSHendrik Brueckner 	char name[9];
110044a01d5bSHendrik Brueckner 	int rc;
110144a01d5bSHendrik Brueckner 
110244a01d5bSHendrik Brueckner 	priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL);
110344a01d5bSHendrik Brueckner 	if (!priv)
110444a01d5bSHendrik Brueckner 		return -ENOMEM;
110544a01d5bSHendrik Brueckner 
110644a01d5bSHendrik Brueckner 	spin_lock_init(&priv->lock);
110744a01d5bSHendrik Brueckner 	INIT_LIST_HEAD(&priv->tty_outqueue);
110844a01d5bSHendrik Brueckner 	INIT_LIST_HEAD(&priv->tty_inqueue);
1109c45ce4b5SHendrik Brueckner 	INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work);
1110c45ce4b5SHendrik Brueckner 	init_waitqueue_head(&priv->sndbuf_waitq);
1111c45ce4b5SHendrik Brueckner 
1112c45ce4b5SHendrik Brueckner 	priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL);
1113c45ce4b5SHendrik Brueckner 	if (!priv->sndbuf) {
1114c45ce4b5SHendrik Brueckner 		kfree(priv);
1115c45ce4b5SHendrik Brueckner 		return -ENOMEM;
1116c45ce4b5SHendrik Brueckner 	}
111744a01d5bSHendrik Brueckner 
11186c089fd3SHendrik Brueckner 	/* set console flag */
11196c089fd3SHendrik Brueckner 	priv->is_console = is_console;
11206c089fd3SHendrik Brueckner 
11210259162eSHendrik Brueckner 	/* allocate hvc device */
1122c45ce4b5SHendrik Brueckner 	priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /*		  PAGE_SIZE */
1123c45ce4b5SHendrik Brueckner 			      HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256);
112444a01d5bSHendrik Brueckner 	if (IS_ERR(priv->hvc)) {
112544a01d5bSHendrik Brueckner 		rc = PTR_ERR(priv->hvc);
11260259162eSHendrik Brueckner 		goto out_error_hvc;
112744a01d5bSHendrik Brueckner 	}
112844a01d5bSHendrik Brueckner 
112917e19f04SHendrik Brueckner 	/* notify HVC thread instead of using polling */
1130c45ce4b5SHendrik Brueckner 	priv->hvc->irq_requested = 1;
1131c45ce4b5SHendrik Brueckner 
113244a01d5bSHendrik Brueckner 	/* setup iucv related information */
11332dc184c0SHendrik Brueckner 	snprintf(name, 9, "lnxhvc%-2d", id);
113444a01d5bSHendrik Brueckner 	memcpy(priv->srv_name, name, 8);
113544a01d5bSHendrik Brueckner 	ASCEBC(priv->srv_name, 8);
113644a01d5bSHendrik Brueckner 
11370259162eSHendrik Brueckner 	/* create and setup device */
11380259162eSHendrik Brueckner 	priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL);
11390259162eSHendrik Brueckner 	if (!priv->dev) {
11400259162eSHendrik Brueckner 		rc = -ENOMEM;
11410259162eSHendrik Brueckner 		goto out_error_dev;
11420259162eSHendrik Brueckner 	}
11430259162eSHendrik Brueckner 	dev_set_name(priv->dev, "hvc_iucv%d", id);
11440259162eSHendrik Brueckner 	dev_set_drvdata(priv->dev, priv);
11450259162eSHendrik Brueckner 	priv->dev->bus = &iucv_bus;
11460259162eSHendrik Brueckner 	priv->dev->parent = iucv_root;
11470259162eSHendrik Brueckner 	priv->dev->driver = &hvc_iucv_driver;
1148f1206badSHendrik Brueckner 	priv->dev->groups = hvc_iucv_dev_attr_groups;
11490259162eSHendrik Brueckner 	priv->dev->release = (void (*)(struct device *)) kfree;
11500259162eSHendrik Brueckner 	rc = device_register(priv->dev);
11510259162eSHendrik Brueckner 	if (rc) {
1152c6304933SSebastian Ott 		put_device(priv->dev);
11530259162eSHendrik Brueckner 		goto out_error_dev;
11540259162eSHendrik Brueckner 	}
11550259162eSHendrik Brueckner 
115644a01d5bSHendrik Brueckner 	hvc_iucv_table[id] = priv;
115744a01d5bSHendrik Brueckner 	return 0;
11580259162eSHendrik Brueckner 
11590259162eSHendrik Brueckner out_error_dev:
11600259162eSHendrik Brueckner 	hvc_remove(priv->hvc);
11610259162eSHendrik Brueckner out_error_hvc:
11620259162eSHendrik Brueckner 	free_page((unsigned long) priv->sndbuf);
11630259162eSHendrik Brueckner 	kfree(priv);
11640259162eSHendrik Brueckner 
11650259162eSHendrik Brueckner 	return rc;
11660259162eSHendrik Brueckner }
11670259162eSHendrik Brueckner 
11680259162eSHendrik Brueckner /**
11690259162eSHendrik Brueckner  * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances
11700259162eSHendrik Brueckner  */
11710259162eSHendrik Brueckner static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv)
11720259162eSHendrik Brueckner {
11730259162eSHendrik Brueckner 	hvc_remove(priv->hvc);
11740259162eSHendrik Brueckner 	device_unregister(priv->dev);
11750259162eSHendrik Brueckner 	free_page((unsigned long) priv->sndbuf);
11760259162eSHendrik Brueckner 	kfree(priv);
117744a01d5bSHendrik Brueckner }
117844a01d5bSHendrik Brueckner 
117944a01d5bSHendrik Brueckner /**
1180431429ffSHendrik Brueckner  * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID
1181431429ffSHendrik Brueckner  * @filter:	String containing a comma-separated list of z/VM user IDs
1182926a7336SHendrik Brueckner  * @dest:	Location where to store the parsed z/VM user ID
1183431429ffSHendrik Brueckner  */
1184431429ffSHendrik Brueckner static const char *hvc_iucv_parse_filter(const char *filter, char *dest)
1185431429ffSHendrik Brueckner {
1186431429ffSHendrik Brueckner 	const char *nextdelim, *residual;
1187431429ffSHendrik Brueckner 	size_t len;
1188431429ffSHendrik Brueckner 
1189431429ffSHendrik Brueckner 	nextdelim = strchr(filter, ',');
1190431429ffSHendrik Brueckner 	if (nextdelim) {
1191431429ffSHendrik Brueckner 		len = nextdelim - filter;
1192431429ffSHendrik Brueckner 		residual = nextdelim + 1;
1193431429ffSHendrik Brueckner 	} else {
1194431429ffSHendrik Brueckner 		len = strlen(filter);
1195431429ffSHendrik Brueckner 		residual = filter + len;
1196431429ffSHendrik Brueckner 	}
1197431429ffSHendrik Brueckner 
1198431429ffSHendrik Brueckner 	if (len == 0)
1199431429ffSHendrik Brueckner 		return ERR_PTR(-EINVAL);
1200431429ffSHendrik Brueckner 
1201431429ffSHendrik Brueckner 	/* check for '\n' (if called from sysfs) */
1202431429ffSHendrik Brueckner 	if (filter[len - 1] == '\n')
1203431429ffSHendrik Brueckner 		len--;
1204431429ffSHendrik Brueckner 
1205926a7336SHendrik Brueckner 	/* prohibit filter entries containing the wildcard character only */
1206926a7336SHendrik Brueckner 	if (len == 1 && *filter == FILTER_WILDCARD_CHAR)
1207926a7336SHendrik Brueckner 		return ERR_PTR(-EINVAL);
1208926a7336SHendrik Brueckner 
1209431429ffSHendrik Brueckner 	if (len > 8)
1210431429ffSHendrik Brueckner 		return ERR_PTR(-EINVAL);
1211431429ffSHendrik Brueckner 
1212431429ffSHendrik Brueckner 	/* pad with blanks and save upper case version of user ID */
1213431429ffSHendrik Brueckner 	memset(dest, ' ', 8);
1214431429ffSHendrik Brueckner 	while (len--)
1215431429ffSHendrik Brueckner 		dest[len] = toupper(filter[len]);
1216431429ffSHendrik Brueckner 	return residual;
1217431429ffSHendrik Brueckner }
1218431429ffSHendrik Brueckner 
1219431429ffSHendrik Brueckner /**
1220431429ffSHendrik Brueckner  * hvc_iucv_setup_filter() - Set up z/VM user ID filter
1221431429ffSHendrik Brueckner  * @filter:	String consisting of a comma-separated list of z/VM user IDs
1222431429ffSHendrik Brueckner  *
1223431429ffSHendrik Brueckner  * The function parses the @filter string and creates an array containing
1224431429ffSHendrik Brueckner  * the list of z/VM user ID filter entries.
1225431429ffSHendrik Brueckner  * Return code 0 means success, -EINVAL if the filter is syntactically
1226431429ffSHendrik Brueckner  * incorrect, -ENOMEM if there was not enough memory to allocate the
1227431429ffSHendrik Brueckner  * filter list array, or -ENOSPC if too many z/VM user IDs have been specified.
1228431429ffSHendrik Brueckner  */
1229431429ffSHendrik Brueckner static int hvc_iucv_setup_filter(const char *val)
1230431429ffSHendrik Brueckner {
1231431429ffSHendrik Brueckner 	const char *residual;
1232431429ffSHendrik Brueckner 	int err;
1233431429ffSHendrik Brueckner 	size_t size, count;
1234431429ffSHendrik Brueckner 	void *array, *old_filter;
1235431429ffSHendrik Brueckner 
1236431429ffSHendrik Brueckner 	count = strlen(val);
1237431429ffSHendrik Brueckner 	if (count == 0 || (count == 1 && val[0] == '\n')) {
1238431429ffSHendrik Brueckner 		size  = 0;
1239431429ffSHendrik Brueckner 		array = NULL;
1240431429ffSHendrik Brueckner 		goto out_replace_filter;	/* clear filter */
1241431429ffSHendrik Brueckner 	}
1242431429ffSHendrik Brueckner 
1243431429ffSHendrik Brueckner 	/* count user IDs in order to allocate sufficient memory */
1244431429ffSHendrik Brueckner 	size = 1;
1245431429ffSHendrik Brueckner 	residual = val;
1246431429ffSHendrik Brueckner 	while ((residual = strchr(residual, ',')) != NULL) {
1247431429ffSHendrik Brueckner 		residual++;
1248431429ffSHendrik Brueckner 		size++;
1249431429ffSHendrik Brueckner 	}
1250431429ffSHendrik Brueckner 
1251431429ffSHendrik Brueckner 	/* check if the specified list exceeds the filter limit */
1252431429ffSHendrik Brueckner 	if (size > MAX_VMID_FILTER)
1253431429ffSHendrik Brueckner 		return -ENOSPC;
1254431429ffSHendrik Brueckner 
1255*6396bb22SKees Cook 	array = kcalloc(size, 8, GFP_KERNEL);
1256431429ffSHendrik Brueckner 	if (!array)
1257431429ffSHendrik Brueckner 		return -ENOMEM;
1258431429ffSHendrik Brueckner 
1259431429ffSHendrik Brueckner 	count = size;
1260431429ffSHendrik Brueckner 	residual = val;
1261431429ffSHendrik Brueckner 	while (*residual && count) {
1262431429ffSHendrik Brueckner 		residual = hvc_iucv_parse_filter(residual,
1263431429ffSHendrik Brueckner 						 array + ((size - count) * 8));
1264431429ffSHendrik Brueckner 		if (IS_ERR(residual)) {
1265431429ffSHendrik Brueckner 			err = PTR_ERR(residual);
1266431429ffSHendrik Brueckner 			kfree(array);
1267431429ffSHendrik Brueckner 			goto out_err;
1268431429ffSHendrik Brueckner 		}
1269431429ffSHendrik Brueckner 		count--;
1270431429ffSHendrik Brueckner 	}
1271431429ffSHendrik Brueckner 
1272431429ffSHendrik Brueckner out_replace_filter:
1273431429ffSHendrik Brueckner 	write_lock_bh(&hvc_iucv_filter_lock);
1274431429ffSHendrik Brueckner 	old_filter = hvc_iucv_filter;
1275431429ffSHendrik Brueckner 	hvc_iucv_filter_size = size;
1276431429ffSHendrik Brueckner 	hvc_iucv_filter = array;
1277431429ffSHendrik Brueckner 	write_unlock_bh(&hvc_iucv_filter_lock);
1278431429ffSHendrik Brueckner 	kfree(old_filter);
1279431429ffSHendrik Brueckner 
1280431429ffSHendrik Brueckner 	err = 0;
1281431429ffSHendrik Brueckner out_err:
1282431429ffSHendrik Brueckner 	return err;
1283431429ffSHendrik Brueckner }
1284431429ffSHendrik Brueckner 
1285431429ffSHendrik Brueckner /**
1286431429ffSHendrik Brueckner  * param_set_vmidfilter() - Set z/VM user ID filter parameter
1287431429ffSHendrik Brueckner  * @val:	String consisting of a comma-separated list of z/VM user IDs
1288431429ffSHendrik Brueckner  * @kp:		Kernel parameter pointing to hvc_iucv_filter array
1289431429ffSHendrik Brueckner  *
1290431429ffSHendrik Brueckner  * The function sets up the z/VM user ID filter specified as comma-separated
1291431429ffSHendrik Brueckner  * list of user IDs in @val.
1292431429ffSHendrik Brueckner  * Note: If it is called early in the boot process, @val is stored and
1293431429ffSHendrik Brueckner  *	 parsed later in hvc_iucv_init().
1294431429ffSHendrik Brueckner  */
1295549a8a03SSachin Sant static int param_set_vmidfilter(const char *val, const struct kernel_param *kp)
1296431429ffSHendrik Brueckner {
1297431429ffSHendrik Brueckner 	int rc;
1298431429ffSHendrik Brueckner 
1299431429ffSHendrik Brueckner 	if (!MACHINE_IS_VM || !hvc_iucv_devices)
1300431429ffSHendrik Brueckner 		return -ENODEV;
1301431429ffSHendrik Brueckner 
1302431429ffSHendrik Brueckner 	if (!val)
1303431429ffSHendrik Brueckner 		return -EINVAL;
1304431429ffSHendrik Brueckner 
1305431429ffSHendrik Brueckner 	rc = 0;
1306431429ffSHendrik Brueckner 	if (slab_is_available())
1307431429ffSHendrik Brueckner 		rc = hvc_iucv_setup_filter(val);
1308431429ffSHendrik Brueckner 	else
1309431429ffSHendrik Brueckner 		hvc_iucv_filter_string = val;	/* defer... */
1310431429ffSHendrik Brueckner 	return rc;
1311431429ffSHendrik Brueckner }
1312431429ffSHendrik Brueckner 
1313431429ffSHendrik Brueckner /**
1314431429ffSHendrik Brueckner  * param_get_vmidfilter() - Get z/VM user ID filter
1315431429ffSHendrik Brueckner  * @buffer:	Buffer to store z/VM user ID filter,
1316431429ffSHendrik Brueckner  *		(buffer size assumption PAGE_SIZE)
1317431429ffSHendrik Brueckner  * @kp:		Kernel parameter pointing to the hvc_iucv_filter array
1318431429ffSHendrik Brueckner  *
1319431429ffSHendrik Brueckner  * The function stores the filter as a comma-separated list of z/VM user IDs
1320431429ffSHendrik Brueckner  * in @buffer. Typically, sysfs routines call this function for attr show.
1321431429ffSHendrik Brueckner  */
1322549a8a03SSachin Sant static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp)
1323431429ffSHendrik Brueckner {
1324431429ffSHendrik Brueckner 	int rc;
1325431429ffSHendrik Brueckner 	size_t index, len;
1326431429ffSHendrik Brueckner 	void *start, *end;
1327431429ffSHendrik Brueckner 
1328431429ffSHendrik Brueckner 	if (!MACHINE_IS_VM || !hvc_iucv_devices)
1329431429ffSHendrik Brueckner 		return -ENODEV;
1330431429ffSHendrik Brueckner 
1331431429ffSHendrik Brueckner 	rc = 0;
1332431429ffSHendrik Brueckner 	read_lock_bh(&hvc_iucv_filter_lock);
1333431429ffSHendrik Brueckner 	for (index = 0; index < hvc_iucv_filter_size; index++) {
1334431429ffSHendrik Brueckner 		start = hvc_iucv_filter + (8 * index);
1335431429ffSHendrik Brueckner 		end   = memchr(start, ' ', 8);
1336431429ffSHendrik Brueckner 		len   = (end) ? end - start : 8;
1337431429ffSHendrik Brueckner 		memcpy(buffer + rc, start, len);
1338431429ffSHendrik Brueckner 		rc += len;
1339431429ffSHendrik Brueckner 		buffer[rc++] = ',';
1340431429ffSHendrik Brueckner 	}
1341431429ffSHendrik Brueckner 	read_unlock_bh(&hvc_iucv_filter_lock);
1342431429ffSHendrik Brueckner 	if (rc)
1343431429ffSHendrik Brueckner 		buffer[--rc] = '\0';	/* replace last comma and update rc */
1344431429ffSHendrik Brueckner 	return rc;
1345431429ffSHendrik Brueckner }
1346431429ffSHendrik Brueckner 
1347431429ffSHendrik Brueckner #define param_check_vmidfilter(name, p) __param_check(name, p, void)
1348431429ffSHendrik Brueckner 
13499c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_vmidfilter = {
1350549a8a03SSachin Sant 	.set = param_set_vmidfilter,
1351549a8a03SSachin Sant 	.get = param_get_vmidfilter,
1352549a8a03SSachin Sant };
1353549a8a03SSachin Sant 
1354431429ffSHendrik Brueckner /**
135517e19f04SHendrik Brueckner  * hvc_iucv_init() - z/VM IUCV HVC device driver initialization
135644a01d5bSHendrik Brueckner  */
135744a01d5bSHendrik Brueckner static int __init hvc_iucv_init(void)
135844a01d5bSHendrik Brueckner {
13596c089fd3SHendrik Brueckner 	int rc;
13606c089fd3SHendrik Brueckner 	unsigned int i;
136144a01d5bSHendrik Brueckner 
1362431429ffSHendrik Brueckner 	if (!hvc_iucv_devices)
1363431429ffSHendrik Brueckner 		return -ENODEV;
1364431429ffSHendrik Brueckner 
136544a01d5bSHendrik Brueckner 	if (!MACHINE_IS_VM) {
136682f3a79bSHendrik Brueckner 		pr_notice("The z/VM IUCV HVC device driver cannot "
1367c45ce4b5SHendrik Brueckner 			   "be used without z/VM\n");
1368431429ffSHendrik Brueckner 		rc = -ENODEV;
1369431429ffSHendrik Brueckner 		goto out_error;
137044a01d5bSHendrik Brueckner 	}
137144a01d5bSHendrik Brueckner 
137282f3a79bSHendrik Brueckner 	if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) {
137382f3a79bSHendrik Brueckner 		pr_err("%lu is not a valid value for the hvc_iucv= "
137482f3a79bSHendrik Brueckner 			"kernel parameter\n", hvc_iucv_devices);
1375431429ffSHendrik Brueckner 		rc = -EINVAL;
1376431429ffSHendrik Brueckner 		goto out_error;
1377431429ffSHendrik Brueckner 	}
1378431429ffSHendrik Brueckner 
13790259162eSHendrik Brueckner 	/* register IUCV HVC device driver */
13800259162eSHendrik Brueckner 	rc = driver_register(&hvc_iucv_driver);
13810259162eSHendrik Brueckner 	if (rc)
13820259162eSHendrik Brueckner 		goto out_error;
13830259162eSHendrik Brueckner 
1384431429ffSHendrik Brueckner 	/* parse hvc_iucv_allow string and create z/VM user ID filter list */
1385431429ffSHendrik Brueckner 	if (hvc_iucv_filter_string) {
1386431429ffSHendrik Brueckner 		rc = hvc_iucv_setup_filter(hvc_iucv_filter_string);
1387431429ffSHendrik Brueckner 		switch (rc) {
1388431429ffSHendrik Brueckner 		case 0:
1389431429ffSHendrik Brueckner 			break;
1390431429ffSHendrik Brueckner 		case -ENOMEM:
1391431429ffSHendrik Brueckner 			pr_err("Allocating memory failed with "
1392431429ffSHendrik Brueckner 				"reason code=%d\n", 3);
1393431429ffSHendrik Brueckner 			goto out_error;
1394431429ffSHendrik Brueckner 		case -EINVAL:
1395431429ffSHendrik Brueckner 			pr_err("hvc_iucv_allow= does not specify a valid "
1396431429ffSHendrik Brueckner 				"z/VM user ID list\n");
1397431429ffSHendrik Brueckner 			goto out_error;
1398431429ffSHendrik Brueckner 		case -ENOSPC:
1399431429ffSHendrik Brueckner 			pr_err("hvc_iucv_allow= specifies too many "
1400431429ffSHendrik Brueckner 				"z/VM user IDs\n");
1401431429ffSHendrik Brueckner 			goto out_error;
1402431429ffSHendrik Brueckner 		default:
1403431429ffSHendrik Brueckner 			goto out_error;
1404431429ffSHendrik Brueckner 		}
140582f3a79bSHendrik Brueckner 	}
140644a01d5bSHendrik Brueckner 
140744a01d5bSHendrik Brueckner 	hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT,
140844a01d5bSHendrik Brueckner 					   sizeof(struct iucv_tty_buffer),
140944a01d5bSHendrik Brueckner 					   0, 0, NULL);
141044a01d5bSHendrik Brueckner 	if (!hvc_iucv_buffer_cache) {
1411c45ce4b5SHendrik Brueckner 		pr_err("Allocating memory failed with reason code=%d\n", 1);
1412431429ffSHendrik Brueckner 		rc = -ENOMEM;
1413431429ffSHendrik Brueckner 		goto out_error;
141444a01d5bSHendrik Brueckner 	}
141544a01d5bSHendrik Brueckner 
141644a01d5bSHendrik Brueckner 	hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR,
141744a01d5bSHendrik Brueckner 						    hvc_iucv_buffer_cache);
141844a01d5bSHendrik Brueckner 	if (!hvc_iucv_mempool) {
1419c45ce4b5SHendrik Brueckner 		pr_err("Allocating memory failed with reason code=%d\n", 2);
142044a01d5bSHendrik Brueckner 		kmem_cache_destroy(hvc_iucv_buffer_cache);
1421431429ffSHendrik Brueckner 		rc = -ENOMEM;
1422431429ffSHendrik Brueckner 		goto out_error;
142344a01d5bSHendrik Brueckner 	}
142444a01d5bSHendrik Brueckner 
142568c6b3d2SHendrik Brueckner 	/* register the first terminal device as console
142668c6b3d2SHendrik Brueckner 	 * (must be done before allocating hvc terminal devices) */
14276c089fd3SHendrik Brueckner 	rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops);
14286c089fd3SHendrik Brueckner 	if (rc) {
14296c089fd3SHendrik Brueckner 		pr_err("Registering HVC terminal device as "
143068c6b3d2SHendrik Brueckner 		       "Linux console failed\n");
14316c089fd3SHendrik Brueckner 		goto out_error_memory;
14326c089fd3SHendrik Brueckner 	}
143368c6b3d2SHendrik Brueckner 
143444a01d5bSHendrik Brueckner 	/* allocate hvc_iucv_private structs */
143544a01d5bSHendrik Brueckner 	for (i = 0; i < hvc_iucv_devices; i++) {
14366c089fd3SHendrik Brueckner 		rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0);
143744a01d5bSHendrik Brueckner 		if (rc) {
1438c45ce4b5SHendrik Brueckner 			pr_err("Creating a new HVC terminal device "
1439c45ce4b5SHendrik Brueckner 				"failed with error code=%d\n", rc);
144044a01d5bSHendrik Brueckner 			goto out_error_hvc;
144144a01d5bSHendrik Brueckner 		}
144244a01d5bSHendrik Brueckner 	}
144344a01d5bSHendrik Brueckner 
144444a01d5bSHendrik Brueckner 	/* register IUCV callback handler */
144544a01d5bSHendrik Brueckner 	rc = iucv_register(&hvc_iucv_handler, 0);
144644a01d5bSHendrik Brueckner 	if (rc) {
1447c45ce4b5SHendrik Brueckner 		pr_err("Registering IUCV handlers failed with error code=%d\n",
1448c45ce4b5SHendrik Brueckner 			rc);
1449c77f7cf7SHendrik Brueckner 		goto out_error_hvc;
145044a01d5bSHendrik Brueckner 	}
145144a01d5bSHendrik Brueckner 
145244a01d5bSHendrik Brueckner 	return 0;
145344a01d5bSHendrik Brueckner 
145444a01d5bSHendrik Brueckner out_error_hvc:
145544a01d5bSHendrik Brueckner 	for (i = 0; i < hvc_iucv_devices; i++)
14560259162eSHendrik Brueckner 		if (hvc_iucv_table[i])
14570259162eSHendrik Brueckner 			hvc_iucv_destroy(hvc_iucv_table[i]);
14586c089fd3SHendrik Brueckner out_error_memory:
145944a01d5bSHendrik Brueckner 	mempool_destroy(hvc_iucv_mempool);
146044a01d5bSHendrik Brueckner 	kmem_cache_destroy(hvc_iucv_buffer_cache);
1461431429ffSHendrik Brueckner out_error:
14620259162eSHendrik Brueckner 	kfree(hvc_iucv_filter);
1463431429ffSHendrik Brueckner 	hvc_iucv_devices = 0; /* ensure that we do not provide any device */
146444a01d5bSHendrik Brueckner 	return rc;
146544a01d5bSHendrik Brueckner }
146644a01d5bSHendrik Brueckner 
146744a01d5bSHendrik Brueckner /**
146844a01d5bSHendrik Brueckner  * hvc_iucv_config() - Parsing of hvc_iucv=  kernel command line parameter
146944a01d5bSHendrik Brueckner  * @val:	Parameter value (numeric)
147044a01d5bSHendrik Brueckner  */
147144a01d5bSHendrik Brueckner static	int __init hvc_iucv_config(char *val)
147244a01d5bSHendrik Brueckner {
147386b40567SJingoo Han 	 return kstrtoul(val, 10, &hvc_iucv_devices);
147444a01d5bSHendrik Brueckner }
147544a01d5bSHendrik Brueckner 
147644a01d5bSHendrik Brueckner 
147768c6b3d2SHendrik Brueckner device_initcall(hvc_iucv_init);
147844a01d5bSHendrik Brueckner __setup("hvc_iucv=", hvc_iucv_config);
1479431429ffSHendrik Brueckner core_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640);
1480