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