xref: /openbmc/linux/drivers/tty/hvc/hvsi_lib.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
217bdc6c0SBenjamin Herrenschmidt #include <linux/types.h>
317bdc6c0SBenjamin Herrenschmidt #include <linux/delay.h>
417bdc6c0SBenjamin Herrenschmidt #include <linux/slab.h>
517bdc6c0SBenjamin Herrenschmidt #include <linux/console.h>
617bdc6c0SBenjamin Herrenschmidt #include <asm/hvsi.h>
717bdc6c0SBenjamin Herrenschmidt 
817bdc6c0SBenjamin Herrenschmidt #include "hvc_console.h"
917bdc6c0SBenjamin Herrenschmidt 
hvsi_send_packet(struct hvsi_priv * pv,struct hvsi_header * packet)1017bdc6c0SBenjamin Herrenschmidt static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet)
1117bdc6c0SBenjamin Herrenschmidt {
1299fc1d91SBenjamin Herrenschmidt 	packet->seqno = cpu_to_be16(atomic_inc_return(&pv->seqno));
1317bdc6c0SBenjamin Herrenschmidt 
1417bdc6c0SBenjamin Herrenschmidt 	/* Assumes that always succeeds, works in practice */
1517bdc6c0SBenjamin Herrenschmidt 	return pv->put_chars(pv->termno, (char *)packet, packet->len);
1617bdc6c0SBenjamin Herrenschmidt }
1717bdc6c0SBenjamin Herrenschmidt 
hvsi_start_handshake(struct hvsi_priv * pv)1817bdc6c0SBenjamin Herrenschmidt static void hvsi_start_handshake(struct hvsi_priv *pv)
1917bdc6c0SBenjamin Herrenschmidt {
2017bdc6c0SBenjamin Herrenschmidt 	struct hvsi_query q;
2117bdc6c0SBenjamin Herrenschmidt 
2217bdc6c0SBenjamin Herrenschmidt 	/* Reset state */
2317bdc6c0SBenjamin Herrenschmidt 	pv->established = 0;
2417bdc6c0SBenjamin Herrenschmidt 	atomic_set(&pv->seqno, 0);
2517bdc6c0SBenjamin Herrenschmidt 
2617bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: Handshaking started\n", pv->termno);
2717bdc6c0SBenjamin Herrenschmidt 
2817bdc6c0SBenjamin Herrenschmidt 	/* Send version query */
2917bdc6c0SBenjamin Herrenschmidt 	q.hdr.type = VS_QUERY_PACKET_HEADER;
3017bdc6c0SBenjamin Herrenschmidt 	q.hdr.len = sizeof(struct hvsi_query);
3199fc1d91SBenjamin Herrenschmidt 	q.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER);
3217bdc6c0SBenjamin Herrenschmidt 	hvsi_send_packet(pv, &q.hdr);
3317bdc6c0SBenjamin Herrenschmidt }
3417bdc6c0SBenjamin Herrenschmidt 
hvsi_send_close(struct hvsi_priv * pv)3517bdc6c0SBenjamin Herrenschmidt static int hvsi_send_close(struct hvsi_priv *pv)
3617bdc6c0SBenjamin Herrenschmidt {
3717bdc6c0SBenjamin Herrenschmidt 	struct hvsi_control ctrl;
3817bdc6c0SBenjamin Herrenschmidt 
3917bdc6c0SBenjamin Herrenschmidt 	pv->established = 0;
4017bdc6c0SBenjamin Herrenschmidt 
4117bdc6c0SBenjamin Herrenschmidt 	ctrl.hdr.type = VS_CONTROL_PACKET_HEADER;
4217bdc6c0SBenjamin Herrenschmidt 	ctrl.hdr.len = sizeof(struct hvsi_control);
4399fc1d91SBenjamin Herrenschmidt 	ctrl.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL);
4417bdc6c0SBenjamin Herrenschmidt 	return hvsi_send_packet(pv, &ctrl.hdr);
4517bdc6c0SBenjamin Herrenschmidt }
4617bdc6c0SBenjamin Herrenschmidt 
hvsi_cd_change(struct hvsi_priv * pv,int cd)4717bdc6c0SBenjamin Herrenschmidt static void hvsi_cd_change(struct hvsi_priv *pv, int cd)
4817bdc6c0SBenjamin Herrenschmidt {
4917bdc6c0SBenjamin Herrenschmidt 	if (cd)
5017bdc6c0SBenjamin Herrenschmidt 		pv->mctrl |= TIOCM_CD;
5117bdc6c0SBenjamin Herrenschmidt 	else {
5217bdc6c0SBenjamin Herrenschmidt 		pv->mctrl &= ~TIOCM_CD;
5317bdc6c0SBenjamin Herrenschmidt 
5417bdc6c0SBenjamin Herrenschmidt 		/* We copy the existing hvsi driver semantics
5517bdc6c0SBenjamin Herrenschmidt 		 * here which are to trigger a hangup when
5617bdc6c0SBenjamin Herrenschmidt 		 * we get a carrier loss.
5717bdc6c0SBenjamin Herrenschmidt 		 * Closing our connection to the server will
5817bdc6c0SBenjamin Herrenschmidt 		 * do just that.
5917bdc6c0SBenjamin Herrenschmidt 		 */
6017bdc6c0SBenjamin Herrenschmidt 		if (!pv->is_console && pv->opened) {
6117bdc6c0SBenjamin Herrenschmidt 			pr_devel("HVSI@%x Carrier lost, hanging up !\n",
6217bdc6c0SBenjamin Herrenschmidt 				 pv->termno);
6317bdc6c0SBenjamin Herrenschmidt 			hvsi_send_close(pv);
6417bdc6c0SBenjamin Herrenschmidt 		}
6517bdc6c0SBenjamin Herrenschmidt 	}
6617bdc6c0SBenjamin Herrenschmidt }
6717bdc6c0SBenjamin Herrenschmidt 
hvsi_got_control(struct hvsi_priv * pv)6817bdc6c0SBenjamin Herrenschmidt static void hvsi_got_control(struct hvsi_priv *pv)
6917bdc6c0SBenjamin Herrenschmidt {
7017bdc6c0SBenjamin Herrenschmidt 	struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf;
7117bdc6c0SBenjamin Herrenschmidt 
7299fc1d91SBenjamin Herrenschmidt 	switch (be16_to_cpu(pkt->verb)) {
7317bdc6c0SBenjamin Herrenschmidt 	case VSV_CLOSE_PROTOCOL:
7417bdc6c0SBenjamin Herrenschmidt 		/* We restart the handshaking */
7517bdc6c0SBenjamin Herrenschmidt 		hvsi_start_handshake(pv);
7617bdc6c0SBenjamin Herrenschmidt 		break;
7717bdc6c0SBenjamin Herrenschmidt 	case VSV_MODEM_CTL_UPDATE:
7817bdc6c0SBenjamin Herrenschmidt 		/* Transition of carrier detect */
7999fc1d91SBenjamin Herrenschmidt 		hvsi_cd_change(pv, be32_to_cpu(pkt->word) & HVSI_TSCD);
8017bdc6c0SBenjamin Herrenschmidt 		break;
8117bdc6c0SBenjamin Herrenschmidt 	}
8217bdc6c0SBenjamin Herrenschmidt }
8317bdc6c0SBenjamin Herrenschmidt 
hvsi_got_query(struct hvsi_priv * pv)8417bdc6c0SBenjamin Herrenschmidt static void hvsi_got_query(struct hvsi_priv *pv)
8517bdc6c0SBenjamin Herrenschmidt {
8617bdc6c0SBenjamin Herrenschmidt 	struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf;
8717bdc6c0SBenjamin Herrenschmidt 	struct hvsi_query_response r;
8817bdc6c0SBenjamin Herrenschmidt 
8917bdc6c0SBenjamin Herrenschmidt 	/* We only handle version queries */
9099fc1d91SBenjamin Herrenschmidt 	if (be16_to_cpu(pkt->verb) != VSV_SEND_VERSION_NUMBER)
9117bdc6c0SBenjamin Herrenschmidt 		return;
9217bdc6c0SBenjamin Herrenschmidt 
9317bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: Got version query, sending response...\n",
9417bdc6c0SBenjamin Herrenschmidt 		 pv->termno);
9517bdc6c0SBenjamin Herrenschmidt 
9617bdc6c0SBenjamin Herrenschmidt 	/* Send version response */
9717bdc6c0SBenjamin Herrenschmidt 	r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
9817bdc6c0SBenjamin Herrenschmidt 	r.hdr.len = sizeof(struct hvsi_query_response);
9999fc1d91SBenjamin Herrenschmidt 	r.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER);
10017bdc6c0SBenjamin Herrenschmidt 	r.u.version = HVSI_VERSION;
10117bdc6c0SBenjamin Herrenschmidt 	r.query_seqno = pkt->hdr.seqno;
10217bdc6c0SBenjamin Herrenschmidt 	hvsi_send_packet(pv, &r.hdr);
10317bdc6c0SBenjamin Herrenschmidt 
10417bdc6c0SBenjamin Herrenschmidt 	/* Assume protocol is open now */
10517bdc6c0SBenjamin Herrenschmidt 	pv->established = 1;
10617bdc6c0SBenjamin Herrenschmidt }
10717bdc6c0SBenjamin Herrenschmidt 
hvsi_got_response(struct hvsi_priv * pv)10817bdc6c0SBenjamin Herrenschmidt static void hvsi_got_response(struct hvsi_priv *pv)
10917bdc6c0SBenjamin Herrenschmidt {
11017bdc6c0SBenjamin Herrenschmidt 	struct hvsi_query_response *r =
11117bdc6c0SBenjamin Herrenschmidt 		(struct hvsi_query_response *)pv->inbuf;
11217bdc6c0SBenjamin Herrenschmidt 
11317bdc6c0SBenjamin Herrenschmidt 	switch(r->verb) {
11417bdc6c0SBenjamin Herrenschmidt 	case VSV_SEND_MODEM_CTL_STATUS:
11599fc1d91SBenjamin Herrenschmidt 		hvsi_cd_change(pv, be32_to_cpu(r->u.mctrl_word) & HVSI_TSCD);
11617bdc6c0SBenjamin Herrenschmidt 		pv->mctrl_update = 1;
11717bdc6c0SBenjamin Herrenschmidt 		break;
11817bdc6c0SBenjamin Herrenschmidt 	}
11917bdc6c0SBenjamin Herrenschmidt }
12017bdc6c0SBenjamin Herrenschmidt 
hvsi_check_packet(struct hvsi_priv * pv)12117bdc6c0SBenjamin Herrenschmidt static int hvsi_check_packet(struct hvsi_priv *pv)
12217bdc6c0SBenjamin Herrenschmidt {
12317bdc6c0SBenjamin Herrenschmidt 	u8 len, type;
12417bdc6c0SBenjamin Herrenschmidt 
12517bdc6c0SBenjamin Herrenschmidt 	/* Check header validity. If it's invalid, we ditch
12617bdc6c0SBenjamin Herrenschmidt 	 * the whole buffer and hope we eventually resync
12717bdc6c0SBenjamin Herrenschmidt 	 */
12817bdc6c0SBenjamin Herrenschmidt 	if (pv->inbuf[0] < 0xfc) {
12917bdc6c0SBenjamin Herrenschmidt 		pv->inbuf_len = pv->inbuf_pktlen = 0;
13017bdc6c0SBenjamin Herrenschmidt 		return 0;
13117bdc6c0SBenjamin Herrenschmidt 	}
13217bdc6c0SBenjamin Herrenschmidt 	type = pv->inbuf[0];
13317bdc6c0SBenjamin Herrenschmidt 	len = pv->inbuf[1];
13417bdc6c0SBenjamin Herrenschmidt 
13517bdc6c0SBenjamin Herrenschmidt 	/* Packet incomplete ? */
13617bdc6c0SBenjamin Herrenschmidt 	if (pv->inbuf_len < len)
13717bdc6c0SBenjamin Herrenschmidt 		return 0;
13817bdc6c0SBenjamin Herrenschmidt 
13917bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n",
14017bdc6c0SBenjamin Herrenschmidt 		 pv->termno, type, len);
14117bdc6c0SBenjamin Herrenschmidt 
14217bdc6c0SBenjamin Herrenschmidt 	/* We have a packet, yay ! Handle it */
14317bdc6c0SBenjamin Herrenschmidt 	switch(type) {
14417bdc6c0SBenjamin Herrenschmidt 	case VS_DATA_PACKET_HEADER:
14517bdc6c0SBenjamin Herrenschmidt 		pv->inbuf_pktlen = len - 4;
14617bdc6c0SBenjamin Herrenschmidt 		pv->inbuf_cur = 4;
14717bdc6c0SBenjamin Herrenschmidt 		return 1;
14817bdc6c0SBenjamin Herrenschmidt 	case VS_CONTROL_PACKET_HEADER:
14917bdc6c0SBenjamin Herrenschmidt 		hvsi_got_control(pv);
15017bdc6c0SBenjamin Herrenschmidt 		break;
15117bdc6c0SBenjamin Herrenschmidt 	case VS_QUERY_PACKET_HEADER:
15217bdc6c0SBenjamin Herrenschmidt 		hvsi_got_query(pv);
15317bdc6c0SBenjamin Herrenschmidt 		break;
15417bdc6c0SBenjamin Herrenschmidt 	case VS_QUERY_RESPONSE_PACKET_HEADER:
15517bdc6c0SBenjamin Herrenschmidt 		hvsi_got_response(pv);
15617bdc6c0SBenjamin Herrenschmidt 		break;
15717bdc6c0SBenjamin Herrenschmidt 	}
15817bdc6c0SBenjamin Herrenschmidt 
15917bdc6c0SBenjamin Herrenschmidt 	/* Swallow packet and retry */
16017bdc6c0SBenjamin Herrenschmidt 	pv->inbuf_len -= len;
16117bdc6c0SBenjamin Herrenschmidt 	memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len);
16217bdc6c0SBenjamin Herrenschmidt 	return 1;
16317bdc6c0SBenjamin Herrenschmidt }
16417bdc6c0SBenjamin Herrenschmidt 
hvsi_get_packet(struct hvsi_priv * pv)16517bdc6c0SBenjamin Herrenschmidt static int hvsi_get_packet(struct hvsi_priv *pv)
16617bdc6c0SBenjamin Herrenschmidt {
16717bdc6c0SBenjamin Herrenschmidt 	/* If we have room in the buffer, ask HV for more */
16817bdc6c0SBenjamin Herrenschmidt 	if (pv->inbuf_len < HVSI_INBUF_SIZE)
16917bdc6c0SBenjamin Herrenschmidt 		pv->inbuf_len += pv->get_chars(pv->termno,
17017bdc6c0SBenjamin Herrenschmidt 					     &pv->inbuf[pv->inbuf_len],
17117bdc6c0SBenjamin Herrenschmidt 					     HVSI_INBUF_SIZE - pv->inbuf_len);
17217bdc6c0SBenjamin Herrenschmidt 	/*
17317bdc6c0SBenjamin Herrenschmidt 	 * If we have at least 4 bytes in the buffer, check for
17417bdc6c0SBenjamin Herrenschmidt 	 * a full packet and retry
17517bdc6c0SBenjamin Herrenschmidt 	 */
17617bdc6c0SBenjamin Herrenschmidt 	if (pv->inbuf_len >= 4)
17717bdc6c0SBenjamin Herrenschmidt 		return hvsi_check_packet(pv);
17817bdc6c0SBenjamin Herrenschmidt 	return 0;
17917bdc6c0SBenjamin Herrenschmidt }
18017bdc6c0SBenjamin Herrenschmidt 
hvsilib_get_chars(struct hvsi_priv * pv,char * buf,int count)18187fa35ddSBenjamin Herrenschmidt int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count)
18217bdc6c0SBenjamin Herrenschmidt {
18317bdc6c0SBenjamin Herrenschmidt 	unsigned int tries, read = 0;
18417bdc6c0SBenjamin Herrenschmidt 
18517bdc6c0SBenjamin Herrenschmidt 	if (WARN_ON(!pv))
186daea1175SBenjamin Herrenschmidt 		return -ENXIO;
18717bdc6c0SBenjamin Herrenschmidt 
18817bdc6c0SBenjamin Herrenschmidt 	/* If we aren't open, don't do anything in order to avoid races
18917bdc6c0SBenjamin Herrenschmidt 	 * with connection establishment. The hvc core will call this
19017bdc6c0SBenjamin Herrenschmidt 	 * before we have returned from notifier_add(), and we need to
19117bdc6c0SBenjamin Herrenschmidt 	 * avoid multiple users playing with the receive buffer
19217bdc6c0SBenjamin Herrenschmidt 	 */
19317bdc6c0SBenjamin Herrenschmidt 	if (!pv->opened)
19417bdc6c0SBenjamin Herrenschmidt 		return 0;
19517bdc6c0SBenjamin Herrenschmidt 
19617bdc6c0SBenjamin Herrenschmidt 	/* We try twice, once with what data we have and once more
19717bdc6c0SBenjamin Herrenschmidt 	 * after we try to fetch some more from the hypervisor
19817bdc6c0SBenjamin Herrenschmidt 	 */
19917bdc6c0SBenjamin Herrenschmidt 	for (tries = 1; count && tries < 2; tries++) {
20017bdc6c0SBenjamin Herrenschmidt 		/* Consume existing data packet */
20117bdc6c0SBenjamin Herrenschmidt 		if (pv->inbuf_pktlen) {
20217bdc6c0SBenjamin Herrenschmidt 			unsigned int l = min(count, (int)pv->inbuf_pktlen);
20317bdc6c0SBenjamin Herrenschmidt 			memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l);
20417bdc6c0SBenjamin Herrenschmidt 			pv->inbuf_cur += l;
20517bdc6c0SBenjamin Herrenschmidt 			pv->inbuf_pktlen -= l;
20617bdc6c0SBenjamin Herrenschmidt 			count -= l;
20717bdc6c0SBenjamin Herrenschmidt 			read += l;
20817bdc6c0SBenjamin Herrenschmidt 		}
20917bdc6c0SBenjamin Herrenschmidt 		if (count == 0)
21017bdc6c0SBenjamin Herrenschmidt 			break;
21117bdc6c0SBenjamin Herrenschmidt 
21217bdc6c0SBenjamin Herrenschmidt 		/* Data packet fully consumed, move down remaning data */
21317bdc6c0SBenjamin Herrenschmidt 		if (pv->inbuf_cur) {
21417bdc6c0SBenjamin Herrenschmidt 			pv->inbuf_len -= pv->inbuf_cur;
21517bdc6c0SBenjamin Herrenschmidt 			memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur],
21617bdc6c0SBenjamin Herrenschmidt 				pv->inbuf_len);
21717bdc6c0SBenjamin Herrenschmidt 			pv->inbuf_cur = 0;
21817bdc6c0SBenjamin Herrenschmidt 		}
21917bdc6c0SBenjamin Herrenschmidt 
22017bdc6c0SBenjamin Herrenschmidt 		/* Try to get another packet */
22117bdc6c0SBenjamin Herrenschmidt 		if (hvsi_get_packet(pv))
22217bdc6c0SBenjamin Herrenschmidt 			tries--;
22317bdc6c0SBenjamin Herrenschmidt 	}
22417bdc6c0SBenjamin Herrenschmidt 	if (!pv->established) {
22517bdc6c0SBenjamin Herrenschmidt 		pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno);
22617bdc6c0SBenjamin Herrenschmidt 		return -EPIPE;
22717bdc6c0SBenjamin Herrenschmidt 	}
22817bdc6c0SBenjamin Herrenschmidt 	return read;
22917bdc6c0SBenjamin Herrenschmidt }
23017bdc6c0SBenjamin Herrenschmidt 
hvsilib_put_chars(struct hvsi_priv * pv,const char * buf,int count)23187fa35ddSBenjamin Herrenschmidt int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count)
23217bdc6c0SBenjamin Herrenschmidt {
23317bdc6c0SBenjamin Herrenschmidt 	struct hvsi_data dp;
23417bdc6c0SBenjamin Herrenschmidt 	int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
23517bdc6c0SBenjamin Herrenschmidt 
23617bdc6c0SBenjamin Herrenschmidt 	if (WARN_ON(!pv))
237daea1175SBenjamin Herrenschmidt 		return -ENODEV;
23817bdc6c0SBenjamin Herrenschmidt 
23917bdc6c0SBenjamin Herrenschmidt 	dp.hdr.type = VS_DATA_PACKET_HEADER;
24017bdc6c0SBenjamin Herrenschmidt 	dp.hdr.len = adjcount + sizeof(struct hvsi_header);
24117bdc6c0SBenjamin Herrenschmidt 	memcpy(dp.data, buf, adjcount);
24217bdc6c0SBenjamin Herrenschmidt 	rc = hvsi_send_packet(pv, &dp.hdr);
24317bdc6c0SBenjamin Herrenschmidt 	if (rc <= 0)
24417bdc6c0SBenjamin Herrenschmidt 		return rc;
24517bdc6c0SBenjamin Herrenschmidt 	return adjcount;
24617bdc6c0SBenjamin Herrenschmidt }
24717bdc6c0SBenjamin Herrenschmidt 
maybe_msleep(unsigned long ms)24817bdc6c0SBenjamin Herrenschmidt static void maybe_msleep(unsigned long ms)
24917bdc6c0SBenjamin Herrenschmidt {
25017bdc6c0SBenjamin Herrenschmidt 	/* During early boot, IRQs are disabled, use mdelay */
25117bdc6c0SBenjamin Herrenschmidt 	if (irqs_disabled())
25217bdc6c0SBenjamin Herrenschmidt 		mdelay(ms);
25317bdc6c0SBenjamin Herrenschmidt 	else
25417bdc6c0SBenjamin Herrenschmidt 		msleep(ms);
25517bdc6c0SBenjamin Herrenschmidt }
25617bdc6c0SBenjamin Herrenschmidt 
hvsilib_read_mctrl(struct hvsi_priv * pv)25787fa35ddSBenjamin Herrenschmidt int hvsilib_read_mctrl(struct hvsi_priv *pv)
25817bdc6c0SBenjamin Herrenschmidt {
25917bdc6c0SBenjamin Herrenschmidt 	struct hvsi_query q;
26017bdc6c0SBenjamin Herrenschmidt 	int rc, timeout;
26117bdc6c0SBenjamin Herrenschmidt 
26217bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: Querying modem control status...\n",
26317bdc6c0SBenjamin Herrenschmidt 		 pv->termno);
26417bdc6c0SBenjamin Herrenschmidt 
26517bdc6c0SBenjamin Herrenschmidt 	pv->mctrl_update = 0;
26617bdc6c0SBenjamin Herrenschmidt 	q.hdr.type = VS_QUERY_PACKET_HEADER;
26717bdc6c0SBenjamin Herrenschmidt 	q.hdr.len = sizeof(struct hvsi_query);
26899fc1d91SBenjamin Herrenschmidt 	q.verb = cpu_to_be16(VSV_SEND_MODEM_CTL_STATUS);
26917bdc6c0SBenjamin Herrenschmidt 	rc = hvsi_send_packet(pv, &q.hdr);
27017bdc6c0SBenjamin Herrenschmidt 	if (rc <= 0) {
27117bdc6c0SBenjamin Herrenschmidt 		pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc);
27217bdc6c0SBenjamin Herrenschmidt 		return rc;
27317bdc6c0SBenjamin Herrenschmidt 	}
27417bdc6c0SBenjamin Herrenschmidt 
27517bdc6c0SBenjamin Herrenschmidt 	/* Try for up to 200ms */
27617bdc6c0SBenjamin Herrenschmidt 	for (timeout = 0; timeout < 20; timeout++) {
27717bdc6c0SBenjamin Herrenschmidt 		if (!pv->established)
27817bdc6c0SBenjamin Herrenschmidt 			return -ENXIO;
27917bdc6c0SBenjamin Herrenschmidt 		if (pv->mctrl_update)
28017bdc6c0SBenjamin Herrenschmidt 			return 0;
28117bdc6c0SBenjamin Herrenschmidt 		if (!hvsi_get_packet(pv))
28217bdc6c0SBenjamin Herrenschmidt 			maybe_msleep(10);
28317bdc6c0SBenjamin Herrenschmidt 	}
28417bdc6c0SBenjamin Herrenschmidt 	return -EIO;
28517bdc6c0SBenjamin Herrenschmidt }
28617bdc6c0SBenjamin Herrenschmidt 
hvsilib_write_mctrl(struct hvsi_priv * pv,int dtr)28787fa35ddSBenjamin Herrenschmidt int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr)
28817bdc6c0SBenjamin Herrenschmidt {
28917bdc6c0SBenjamin Herrenschmidt 	struct hvsi_control ctrl;
29017bdc6c0SBenjamin Herrenschmidt 	unsigned short mctrl;
29117bdc6c0SBenjamin Herrenschmidt 
29217bdc6c0SBenjamin Herrenschmidt 	mctrl = pv->mctrl;
29317bdc6c0SBenjamin Herrenschmidt 	if (dtr)
29417bdc6c0SBenjamin Herrenschmidt 		mctrl |= TIOCM_DTR;
29517bdc6c0SBenjamin Herrenschmidt 	else
29617bdc6c0SBenjamin Herrenschmidt 		mctrl &= ~TIOCM_DTR;
29717bdc6c0SBenjamin Herrenschmidt 	if (mctrl == pv->mctrl)
29817bdc6c0SBenjamin Herrenschmidt 		return 0;
29917bdc6c0SBenjamin Herrenschmidt 	pv->mctrl = mctrl;
30017bdc6c0SBenjamin Herrenschmidt 
30117bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: %s DTR...\n", pv->termno,
30217bdc6c0SBenjamin Herrenschmidt 		 dtr ? "Setting" : "Clearing");
30317bdc6c0SBenjamin Herrenschmidt 
30417bdc6c0SBenjamin Herrenschmidt 	ctrl.hdr.type = VS_CONTROL_PACKET_HEADER,
30517bdc6c0SBenjamin Herrenschmidt 	ctrl.hdr.len = sizeof(struct hvsi_control);
30699fc1d91SBenjamin Herrenschmidt 	ctrl.verb = cpu_to_be16(VSV_SET_MODEM_CTL);
30799fc1d91SBenjamin Herrenschmidt 	ctrl.mask = cpu_to_be32(HVSI_TSDTR);
30899fc1d91SBenjamin Herrenschmidt 	ctrl.word = cpu_to_be32(dtr ? HVSI_TSDTR : 0);
30917bdc6c0SBenjamin Herrenschmidt 	return hvsi_send_packet(pv, &ctrl.hdr);
31017bdc6c0SBenjamin Herrenschmidt }
31117bdc6c0SBenjamin Herrenschmidt 
hvsilib_establish(struct hvsi_priv * pv)31287fa35ddSBenjamin Herrenschmidt void hvsilib_establish(struct hvsi_priv *pv)
31317bdc6c0SBenjamin Herrenschmidt {
31417bdc6c0SBenjamin Herrenschmidt 	int timeout;
31517bdc6c0SBenjamin Herrenschmidt 
31617bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: Establishing...\n", pv->termno);
31717bdc6c0SBenjamin Herrenschmidt 
31817bdc6c0SBenjamin Herrenschmidt 	/* Try for up to 200ms, there can be a packet to
31917bdc6c0SBenjamin Herrenschmidt 	 * start the process waiting for us...
32017bdc6c0SBenjamin Herrenschmidt 	 */
32117bdc6c0SBenjamin Herrenschmidt 	for (timeout = 0; timeout < 20; timeout++) {
32217bdc6c0SBenjamin Herrenschmidt 		if (pv->established)
32317bdc6c0SBenjamin Herrenschmidt 			goto established;
32417bdc6c0SBenjamin Herrenschmidt 		if (!hvsi_get_packet(pv))
32517bdc6c0SBenjamin Herrenschmidt 			maybe_msleep(10);
32617bdc6c0SBenjamin Herrenschmidt 	}
32717bdc6c0SBenjamin Herrenschmidt 
32817bdc6c0SBenjamin Herrenschmidt 	/* Failed, send a close connection packet just
32917bdc6c0SBenjamin Herrenschmidt 	 * in case
33017bdc6c0SBenjamin Herrenschmidt 	 */
33117bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x:   ... sending close\n", pv->termno);
33217bdc6c0SBenjamin Herrenschmidt 
33317bdc6c0SBenjamin Herrenschmidt 	hvsi_send_close(pv);
33417bdc6c0SBenjamin Herrenschmidt 
33517bdc6c0SBenjamin Herrenschmidt 	/* Then restart handshake */
33617bdc6c0SBenjamin Herrenschmidt 
33717bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x:   ... restarting handshake\n", pv->termno);
33817bdc6c0SBenjamin Herrenschmidt 
33917bdc6c0SBenjamin Herrenschmidt 	hvsi_start_handshake(pv);
34017bdc6c0SBenjamin Herrenschmidt 
34117bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x:   ... waiting handshake\n", pv->termno);
34217bdc6c0SBenjamin Herrenschmidt 
343d220980bSEugene Surovegin 	/* Try for up to 400ms */
344d220980bSEugene Surovegin 	for (timeout = 0; timeout < 40; timeout++) {
34517bdc6c0SBenjamin Herrenschmidt 		if (pv->established)
34617bdc6c0SBenjamin Herrenschmidt 			goto established;
34717bdc6c0SBenjamin Herrenschmidt 		if (!hvsi_get_packet(pv))
34817bdc6c0SBenjamin Herrenschmidt 			maybe_msleep(10);
34917bdc6c0SBenjamin Herrenschmidt 	}
35017bdc6c0SBenjamin Herrenschmidt 
35117bdc6c0SBenjamin Herrenschmidt 	if (!pv->established) {
35217bdc6c0SBenjamin Herrenschmidt 		pr_devel("HVSI@%x: Timeout handshaking, giving up !\n",
35317bdc6c0SBenjamin Herrenschmidt 			 pv->termno);
35417bdc6c0SBenjamin Herrenschmidt 		return;
35517bdc6c0SBenjamin Herrenschmidt 	}
35617bdc6c0SBenjamin Herrenschmidt  established:
35717bdc6c0SBenjamin Herrenschmidt 	/* Query modem control lines */
35817bdc6c0SBenjamin Herrenschmidt 
35917bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x:   ... established, reading mctrl\n", pv->termno);
36017bdc6c0SBenjamin Herrenschmidt 
36187fa35ddSBenjamin Herrenschmidt 	hvsilib_read_mctrl(pv);
36217bdc6c0SBenjamin Herrenschmidt 
36317bdc6c0SBenjamin Herrenschmidt 	/* Set our own DTR */
36417bdc6c0SBenjamin Herrenschmidt 
36517bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x:   ... setting mctrl\n", pv->termno);
36617bdc6c0SBenjamin Herrenschmidt 
36787fa35ddSBenjamin Herrenschmidt 	hvsilib_write_mctrl(pv, 1);
36817bdc6c0SBenjamin Herrenschmidt 
36917bdc6c0SBenjamin Herrenschmidt 	/* Set the opened flag so reads are allowed */
37017bdc6c0SBenjamin Herrenschmidt 	wmb();
37117bdc6c0SBenjamin Herrenschmidt 	pv->opened = 1;
37217bdc6c0SBenjamin Herrenschmidt }
37317bdc6c0SBenjamin Herrenschmidt 
hvsilib_open(struct hvsi_priv * pv,struct hvc_struct * hp)37487fa35ddSBenjamin Herrenschmidt int hvsilib_open(struct hvsi_priv *pv, struct hvc_struct *hp)
37517bdc6c0SBenjamin Herrenschmidt {
37617bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: open !\n", pv->termno);
37717bdc6c0SBenjamin Herrenschmidt 
37817bdc6c0SBenjamin Herrenschmidt 	/* Keep track of the tty data structure */
37985bbc003SJiri Slaby 	pv->tty = tty_port_tty_get(&hp->port);
38017bdc6c0SBenjamin Herrenschmidt 
38187fa35ddSBenjamin Herrenschmidt 	hvsilib_establish(pv);
38217bdc6c0SBenjamin Herrenschmidt 
38317bdc6c0SBenjamin Herrenschmidt 	return 0;
38417bdc6c0SBenjamin Herrenschmidt }
38517bdc6c0SBenjamin Herrenschmidt 
hvsilib_close(struct hvsi_priv * pv,struct hvc_struct * hp)38687fa35ddSBenjamin Herrenschmidt void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp)
38717bdc6c0SBenjamin Herrenschmidt {
38817bdc6c0SBenjamin Herrenschmidt 	unsigned long flags;
38917bdc6c0SBenjamin Herrenschmidt 
39017bdc6c0SBenjamin Herrenschmidt 	pr_devel("HVSI@%x: close !\n", pv->termno);
39117bdc6c0SBenjamin Herrenschmidt 
39217bdc6c0SBenjamin Herrenschmidt 	if (!pv->is_console) {
39317bdc6c0SBenjamin Herrenschmidt 		pr_devel("HVSI@%x: Not a console, tearing down\n",
39417bdc6c0SBenjamin Herrenschmidt 			 pv->termno);
39517bdc6c0SBenjamin Herrenschmidt 
39617bdc6c0SBenjamin Herrenschmidt 		/* Clear opened, synchronize with khvcd */
39717bdc6c0SBenjamin Herrenschmidt 		spin_lock_irqsave(&hp->lock, flags);
39817bdc6c0SBenjamin Herrenschmidt 		pv->opened = 0;
39917bdc6c0SBenjamin Herrenschmidt 		spin_unlock_irqrestore(&hp->lock, flags);
40017bdc6c0SBenjamin Herrenschmidt 
40117bdc6c0SBenjamin Herrenschmidt 		/* Clear our own DTR */
402adc8d746SAlan Cox 		if (!pv->tty || (pv->tty->termios.c_cflag & HUPCL))
40387fa35ddSBenjamin Herrenschmidt 			hvsilib_write_mctrl(pv, 0);
40417bdc6c0SBenjamin Herrenschmidt 
40517bdc6c0SBenjamin Herrenschmidt 		/* Tear down the connection */
40617bdc6c0SBenjamin Herrenschmidt 		hvsi_send_close(pv);
40717bdc6c0SBenjamin Herrenschmidt 	}
40817bdc6c0SBenjamin Herrenschmidt 
40917bdc6c0SBenjamin Herrenschmidt 	tty_kref_put(pv->tty);
41017bdc6c0SBenjamin Herrenschmidt 	pv->tty = NULL;
41117bdc6c0SBenjamin Herrenschmidt }
41217bdc6c0SBenjamin Herrenschmidt 
hvsilib_init(struct hvsi_priv * pv,int (* get_chars)(uint32_t termno,char * buf,int count),int (* put_chars)(uint32_t termno,const char * buf,int count),int termno,int is_console)41387fa35ddSBenjamin Herrenschmidt void hvsilib_init(struct hvsi_priv *pv,
41417bdc6c0SBenjamin Herrenschmidt 		  int (*get_chars)(uint32_t termno, char *buf, int count),
41517bdc6c0SBenjamin Herrenschmidt 		  int (*put_chars)(uint32_t termno, const char *buf,
41617bdc6c0SBenjamin Herrenschmidt 				   int count),
41717bdc6c0SBenjamin Herrenschmidt 		  int termno, int is_console)
41817bdc6c0SBenjamin Herrenschmidt {
41917bdc6c0SBenjamin Herrenschmidt 	memset(pv, 0, sizeof(*pv));
42017bdc6c0SBenjamin Herrenschmidt 	pv->get_chars = get_chars;
42117bdc6c0SBenjamin Herrenschmidt 	pv->put_chars = put_chars;
42217bdc6c0SBenjamin Herrenschmidt 	pv->termno = termno;
42317bdc6c0SBenjamin Herrenschmidt 	pv->is_console = is_console;
42417bdc6c0SBenjamin Herrenschmidt }
425