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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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