linux/drivers/tty/hvc/hvsi_lib.c
<<
>>
Prefs
   1#include <linux/types.h>
   2#include <linux/delay.h>
   3#include <linux/slab.h>
   4#include <linux/console.h>
   5#include <asm/hvsi.h>
   6
   7#include "hvc_console.h"
   8
   9static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet)
  10{
  11        packet->seqno = cpu_to_be16(atomic_inc_return(&pv->seqno));
  12
  13        /* Assumes that always succeeds, works in practice */
  14        return pv->put_chars(pv->termno, (char *)packet, packet->len);
  15}
  16
  17static void hvsi_start_handshake(struct hvsi_priv *pv)
  18{
  19        struct hvsi_query q;
  20
  21        /* Reset state */
  22        pv->established = 0;
  23        atomic_set(&pv->seqno, 0);
  24
  25        pr_devel("HVSI@%x: Handshaking started\n", pv->termno);
  26
  27        /* Send version query */
  28        q.hdr.type = VS_QUERY_PACKET_HEADER;
  29        q.hdr.len = sizeof(struct hvsi_query);
  30        q.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER);
  31        hvsi_send_packet(pv, &q.hdr);
  32}
  33
  34static int hvsi_send_close(struct hvsi_priv *pv)
  35{
  36        struct hvsi_control ctrl;
  37
  38        pv->established = 0;
  39
  40        ctrl.hdr.type = VS_CONTROL_PACKET_HEADER;
  41        ctrl.hdr.len = sizeof(struct hvsi_control);
  42        ctrl.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL);
  43        return hvsi_send_packet(pv, &ctrl.hdr);
  44}
  45
  46static void hvsi_cd_change(struct hvsi_priv *pv, int cd)
  47{
  48        if (cd)
  49                pv->mctrl |= TIOCM_CD;
  50        else {
  51                pv->mctrl &= ~TIOCM_CD;
  52
  53                /* We copy the existing hvsi driver semantics
  54                 * here which are to trigger a hangup when
  55                 * we get a carrier loss.
  56                 * Closing our connection to the server will
  57                 * do just that.
  58                 */
  59                if (!pv->is_console && pv->opened) {
  60                        pr_devel("HVSI@%x Carrier lost, hanging up !\n",
  61                                 pv->termno);
  62                        hvsi_send_close(pv);
  63                }
  64        }
  65}
  66
  67static void hvsi_got_control(struct hvsi_priv *pv)
  68{
  69        struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf;
  70
  71        switch (be16_to_cpu(pkt->verb)) {
  72        case VSV_CLOSE_PROTOCOL:
  73                /* We restart the handshaking */
  74                hvsi_start_handshake(pv);
  75                break;
  76        case VSV_MODEM_CTL_UPDATE:
  77                /* Transition of carrier detect */
  78                hvsi_cd_change(pv, be32_to_cpu(pkt->word) & HVSI_TSCD);
  79                break;
  80        }
  81}
  82
  83static void hvsi_got_query(struct hvsi_priv *pv)
  84{
  85        struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf;
  86        struct hvsi_query_response r;
  87
  88        /* We only handle version queries */
  89        if (be16_to_cpu(pkt->verb) != VSV_SEND_VERSION_NUMBER)
  90                return;
  91
  92        pr_devel("HVSI@%x: Got version query, sending response...\n",
  93                 pv->termno);
  94
  95        /* Send version response */
  96        r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
  97        r.hdr.len = sizeof(struct hvsi_query_response);
  98        r.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER);
  99        r.u.version = HVSI_VERSION;
 100        r.query_seqno = pkt->hdr.seqno;
 101        hvsi_send_packet(pv, &r.hdr);
 102
 103        /* Assume protocol is open now */
 104        pv->established = 1;
 105}
 106
 107static void hvsi_got_response(struct hvsi_priv *pv)
 108{
 109        struct hvsi_query_response *r =
 110                (struct hvsi_query_response *)pv->inbuf;
 111
 112        switch(r->verb) {
 113        case VSV_SEND_MODEM_CTL_STATUS:
 114                hvsi_cd_change(pv, be32_to_cpu(r->u.mctrl_word) & HVSI_TSCD);
 115                pv->mctrl_update = 1;
 116                break;
 117        }
 118}
 119
 120static int hvsi_check_packet(struct hvsi_priv *pv)
 121{
 122        u8 len, type;
 123
 124        /* Check header validity. If it's invalid, we ditch
 125         * the whole buffer and hope we eventually resync
 126         */
 127        if (pv->inbuf[0] < 0xfc) {
 128                pv->inbuf_len = pv->inbuf_pktlen = 0;
 129                return 0;
 130        }
 131        type = pv->inbuf[0];
 132        len = pv->inbuf[1];
 133
 134        /* Packet incomplete ? */
 135        if (pv->inbuf_len < len)
 136                return 0;
 137
 138        pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n",
 139                 pv->termno, type, len);
 140
 141        /* We have a packet, yay ! Handle it */
 142        switch(type) {
 143        case VS_DATA_PACKET_HEADER:
 144                pv->inbuf_pktlen = len - 4;
 145                pv->inbuf_cur = 4;
 146                return 1;
 147        case VS_CONTROL_PACKET_HEADER:
 148                hvsi_got_control(pv);
 149                break;
 150        case VS_QUERY_PACKET_HEADER:
 151                hvsi_got_query(pv);
 152                break;
 153        case VS_QUERY_RESPONSE_PACKET_HEADER:
 154                hvsi_got_response(pv);
 155                break;
 156        }
 157
 158        /* Swallow packet and retry */
 159        pv->inbuf_len -= len;
 160        memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len);
 161        return 1;
 162}
 163
 164static int hvsi_get_packet(struct hvsi_priv *pv)
 165{
 166        /* If we have room in the buffer, ask HV for more */
 167        if (pv->inbuf_len < HVSI_INBUF_SIZE)
 168                pv->inbuf_len += pv->get_chars(pv->termno,
 169                                             &pv->inbuf[pv->inbuf_len],
 170                                             HVSI_INBUF_SIZE - pv->inbuf_len);
 171        /*
 172         * If we have at least 4 bytes in the buffer, check for
 173         * a full packet and retry
 174         */
 175        if (pv->inbuf_len >= 4)
 176                return hvsi_check_packet(pv);
 177        return 0;
 178}
 179
 180int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count)
 181{
 182        unsigned int tries, read = 0;
 183
 184        if (WARN_ON(!pv))
 185                return -ENXIO;
 186
 187        /* If we aren't open, don't do anything in order to avoid races
 188         * with connection establishment. The hvc core will call this
 189         * before we have returned from notifier_add(), and we need to
 190         * avoid multiple users playing with the receive buffer
 191         */
 192        if (!pv->opened)
 193                return 0;
 194
 195        /* We try twice, once with what data we have and once more
 196         * after we try to fetch some more from the hypervisor
 197         */
 198        for (tries = 1; count && tries < 2; tries++) {
 199                /* Consume existing data packet */
 200                if (pv->inbuf_pktlen) {
 201                        unsigned int l = min(count, (int)pv->inbuf_pktlen);
 202                        memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l);
 203                        pv->inbuf_cur += l;
 204                        pv->inbuf_pktlen -= l;
 205                        count -= l;
 206                        read += l;
 207                }
 208                if (count == 0)
 209                        break;
 210
 211                /* Data packet fully consumed, move down remaning data */
 212                if (pv->inbuf_cur) {
 213                        pv->inbuf_len -= pv->inbuf_cur;
 214                        memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur],
 215                                pv->inbuf_len);
 216                        pv->inbuf_cur = 0;
 217                }
 218
 219                /* Try to get another packet */
 220                if (hvsi_get_packet(pv))
 221                        tries--;
 222        }
 223        if (!pv->established) {
 224                pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno);
 225                return -EPIPE;
 226        }
 227        return read;
 228}
 229
 230int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count)
 231{
 232        struct hvsi_data dp;
 233        int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
 234
 235        if (WARN_ON(!pv))
 236                return -ENODEV;
 237
 238        dp.hdr.type = VS_DATA_PACKET_HEADER;
 239        dp.hdr.len = adjcount + sizeof(struct hvsi_header);
 240        memcpy(dp.data, buf, adjcount);
 241        rc = hvsi_send_packet(pv, &dp.hdr);
 242        if (rc <= 0)
 243                return rc;
 244        return adjcount;
 245}
 246
 247static void maybe_msleep(unsigned long ms)
 248{
 249        /* During early boot, IRQs are disabled, use mdelay */
 250        if (irqs_disabled())
 251                mdelay(ms);
 252        else
 253                msleep(ms);
 254}
 255
 256int hvsilib_read_mctrl(struct hvsi_priv *pv)
 257{
 258        struct hvsi_query q;
 259        int rc, timeout;
 260
 261        pr_devel("HVSI@%x: Querying modem control status...\n",
 262                 pv->termno);
 263
 264        pv->mctrl_update = 0;
 265        q.hdr.type = VS_QUERY_PACKET_HEADER;
 266        q.hdr.len = sizeof(struct hvsi_query);
 267        q.verb = cpu_to_be16(VSV_SEND_MODEM_CTL_STATUS);
 268        rc = hvsi_send_packet(pv, &q.hdr);
 269        if (rc <= 0) {
 270                pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc);
 271                return rc;
 272        }
 273
 274        /* Try for up to 200ms */
 275        for (timeout = 0; timeout < 20; timeout++) {
 276                if (!pv->established)
 277                        return -ENXIO;
 278                if (pv->mctrl_update)
 279                        return 0;
 280                if (!hvsi_get_packet(pv))
 281                        maybe_msleep(10);
 282        }
 283        return -EIO;
 284}
 285
 286int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr)
 287{
 288        struct hvsi_control ctrl;
 289        unsigned short mctrl;
 290
 291        mctrl = pv->mctrl;
 292        if (dtr)
 293                mctrl |= TIOCM_DTR;
 294        else
 295                mctrl &= ~TIOCM_DTR;
 296        if (mctrl == pv->mctrl)
 297                return 0;
 298        pv->mctrl = mctrl;
 299
 300        pr_devel("HVSI@%x: %s DTR...\n", pv->termno,
 301                 dtr ? "Setting" : "Clearing");
 302
 303        ctrl.hdr.type = VS_CONTROL_PACKET_HEADER,
 304        ctrl.hdr.len = sizeof(struct hvsi_control);
 305        ctrl.verb = cpu_to_be16(VSV_SET_MODEM_CTL);
 306        ctrl.mask = cpu_to_be32(HVSI_TSDTR);
 307        ctrl.word = cpu_to_be32(dtr ? HVSI_TSDTR : 0);
 308        return hvsi_send_packet(pv, &ctrl.hdr);
 309}
 310
 311void hvsilib_establish(struct hvsi_priv *pv)
 312{
 313        int timeout;
 314
 315        pr_devel("HVSI@%x: Establishing...\n", pv->termno);
 316
 317        /* Try for up to 200ms, there can be a packet to
 318         * start the process waiting for us...
 319         */
 320        for (timeout = 0; timeout < 20; timeout++) {
 321                if (pv->established)
 322                        goto established;
 323                if (!hvsi_get_packet(pv))
 324                        maybe_msleep(10);
 325        }
 326
 327        /* Failed, send a close connection packet just
 328         * in case
 329         */
 330        pr_devel("HVSI@%x:   ... sending close\n", pv->termno);
 331
 332        hvsi_send_close(pv);
 333
 334        /* Then restart handshake */
 335
 336        pr_devel("HVSI@%x:   ... restarting handshake\n", pv->termno);
 337
 338        hvsi_start_handshake(pv);
 339
 340        pr_devel("HVSI@%x:   ... waiting handshake\n", pv->termno);
 341
 342        /* Try for up to 400ms */
 343        for (timeout = 0; timeout < 40; timeout++) {
 344                if (pv->established)
 345                        goto established;
 346                if (!hvsi_get_packet(pv))
 347                        maybe_msleep(10);
 348        }
 349
 350        if (!pv->established) {
 351                pr_devel("HVSI@%x: Timeout handshaking, giving up !\n",
 352                         pv->termno);
 353                return;
 354        }
 355 established:
 356        /* Query modem control lines */
 357
 358        pr_devel("HVSI@%x:   ... established, reading mctrl\n", pv->termno);
 359
 360        hvsilib_read_mctrl(pv);
 361
 362        /* Set our own DTR */
 363
 364        pr_devel("HVSI@%x:   ... setting mctrl\n", pv->termno);
 365
 366        hvsilib_write_mctrl(pv, 1);
 367
 368        /* Set the opened flag so reads are allowed */
 369        wmb();
 370        pv->opened = 1;
 371}
 372
 373int hvsilib_open(struct hvsi_priv *pv, struct hvc_struct *hp)
 374{
 375        pr_devel("HVSI@%x: open !\n", pv->termno);
 376
 377        /* Keep track of the tty data structure */
 378        pv->tty = tty_port_tty_get(&hp->port);
 379
 380        hvsilib_establish(pv);
 381
 382        return 0;
 383}
 384
 385void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp)
 386{
 387        unsigned long flags;
 388
 389        pr_devel("HVSI@%x: close !\n", pv->termno);
 390
 391        if (!pv->is_console) {
 392                pr_devel("HVSI@%x: Not a console, tearing down\n",
 393                         pv->termno);
 394
 395                /* Clear opened, synchronize with khvcd */
 396                spin_lock_irqsave(&hp->lock, flags);
 397                pv->opened = 0;
 398                spin_unlock_irqrestore(&hp->lock, flags);
 399
 400                /* Clear our own DTR */
 401                if (!pv->tty || (pv->tty->termios.c_cflag & HUPCL))
 402                        hvsilib_write_mctrl(pv, 0);
 403
 404                /* Tear down the connection */
 405                hvsi_send_close(pv);
 406        }
 407
 408        tty_kref_put(pv->tty);
 409        pv->tty = NULL;
 410}
 411
 412void hvsilib_init(struct hvsi_priv *pv,
 413                  int (*get_chars)(uint32_t termno, char *buf, int count),
 414                  int (*put_chars)(uint32_t termno, const char *buf,
 415                                   int count),
 416                  int termno, int is_console)
 417{
 418        memset(pv, 0, sizeof(*pv));
 419        pv->get_chars = get_chars;
 420        pv->put_chars = put_chars;
 421        pv->termno = termno;
 422        pv->is_console = is_console;
 423}
 424