qemu/hw/char/sclpconsole.c
<<
>>
Prefs
   1/*
   2 * SCLP event type
   3 *    Ascii Console Data (VT220 Console)
   4 *
   5 * Copyright IBM, Corp. 2012
   6 *
   7 * Authors:
   8 *  Heinz Graalfs <graalfs@de.ibm.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
  11 * option) any later version.  See the COPYING file in the top-level directory.
  12 *
  13 */
  14
  15#include "qemu/osdep.h"
  16#include "hw/qdev.h"
  17#include "qemu/thread.h"
  18#include "qemu/error-report.h"
  19
  20#include "hw/s390x/sclp.h"
  21#include "hw/s390x/event-facility.h"
  22#include "chardev/char-fe.h"
  23
  24typedef struct ASCIIConsoleData {
  25    EventBufferHeader ebh;
  26    char data[0];
  27} QEMU_PACKED ASCIIConsoleData;
  28
  29/* max size for ASCII data in 4K SCCB page */
  30#define SIZE_BUFFER_VT220 4080
  31
  32typedef struct SCLPConsole {
  33    SCLPEvent event;
  34    CharBackend chr;
  35    uint8_t iov[SIZE_BUFFER_VT220];
  36    uint32_t iov_sclp;      /* offset in buf for SCLP read operation       */
  37    uint32_t iov_bs;        /* offset in buf for char layer read operation */
  38    uint32_t iov_data_len;  /* length of byte stream in buffer             */
  39    uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP     */
  40    bool notify;            /* qemu_notify_event() req'd if true           */
  41} SCLPConsole;
  42
  43#define TYPE_SCLP_CONSOLE "sclpconsole"
  44#define SCLP_CONSOLE(obj) \
  45    OBJECT_CHECK(SCLPConsole, (obj), TYPE_SCLP_CONSOLE)
  46
  47/* character layer call-back functions */
  48
  49/* Return number of bytes that fit into iov buffer */
  50static int chr_can_read(void *opaque)
  51{
  52    SCLPConsole *scon = opaque;
  53    int avail = SIZE_BUFFER_VT220 - scon->iov_data_len;
  54
  55    if (avail == 0) {
  56        scon->notify = true;
  57    }
  58    return avail;
  59}
  60
  61/* Send data from a char device over to the guest */
  62static void chr_read(void *opaque, const uint8_t *buf, int size)
  63{
  64    SCLPConsole *scon = opaque;
  65
  66    assert(scon);
  67    /* read data must fit into current buffer */
  68    assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
  69
  70    /* put byte-stream from character layer into buffer */
  71    memcpy(&scon->iov[scon->iov_bs], buf, size);
  72    scon->iov_data_len += size;
  73    scon->iov_sclp_rest += size;
  74    scon->iov_bs += size;
  75    scon->event.event_pending = true;
  76    sclp_service_interrupt(0);
  77}
  78
  79/* functions to be called by event facility */
  80
  81static bool can_handle_event(uint8_t type)
  82{
  83    return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
  84}
  85
  86static sccb_mask_t send_mask(void)
  87{
  88    return SCLP_EVENT_MASK_MSG_ASCII;
  89}
  90
  91static sccb_mask_t receive_mask(void)
  92{
  93    return SCLP_EVENT_MASK_MSG_ASCII;
  94}
  95
  96/* triggered by SCLP's read_event_data -
  97 * copy console data byte-stream into provided (SCLP) buffer
  98 */
  99static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
 100                             int avail)
 101{
 102    SCLPConsole *cons = SCLP_CONSOLE(event);
 103
 104    /* first byte is hex 0 saying an ascii string follows */
 105    *buf++ = '\0';
 106    avail--;
 107    /* if all data fit into provided SCLP buffer */
 108    if (avail >= cons->iov_sclp_rest) {
 109        /* copy character byte-stream to SCLP buffer */
 110        memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest);
 111        *size = cons->iov_sclp_rest + 1;
 112        cons->iov_sclp = 0;
 113        cons->iov_bs = 0;
 114        cons->iov_data_len = 0;
 115        cons->iov_sclp_rest = 0;
 116        event->event_pending = false;
 117        /* data provided and no more data pending */
 118    } else {
 119        /* if provided buffer is too small, just copy part */
 120        memcpy(buf, &cons->iov[cons->iov_sclp], avail);
 121        *size = avail + 1;
 122        cons->iov_sclp_rest -= avail;
 123        cons->iov_sclp += avail;
 124        /* more data pending */
 125    }
 126    if (cons->notify) {
 127        cons->notify = false;
 128        qemu_notify_event();
 129    }
 130}
 131
 132static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
 133                           int *slen)
 134{
 135    int avail;
 136    size_t src_len;
 137    uint8_t *to;
 138    ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
 139
 140    if (!event->event_pending) {
 141        /* no data pending */
 142        return 0;
 143    }
 144
 145    to = (uint8_t *)&acd->data;
 146    avail = *slen - sizeof(ASCIIConsoleData);
 147    get_console_data(event, to, &src_len, avail);
 148
 149    acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
 150    acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
 151    acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
 152    *slen = avail - src_len;
 153
 154    return 1;
 155}
 156
 157/* triggered by SCLP's write_event_data
 158 *  - write console data to character layer
 159 *  returns < 0 if an error occurred
 160 */
 161static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
 162                                  size_t len)
 163{
 164    SCLPConsole *scon = SCLP_CONSOLE(event);
 165
 166    if (!qemu_chr_fe_backend_connected(&scon->chr)) {
 167        /* If there's no backend, we can just say we consumed all data. */
 168        return len;
 169    }
 170
 171    /* XXX this blocks entire thread. Rewrite to use
 172     * qemu_chr_fe_write and background I/O callbacks */
 173    return qemu_chr_fe_write_all(&scon->chr, buf, len);
 174}
 175
 176static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
 177{
 178    int rc;
 179    int length;
 180    ssize_t written;
 181    ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
 182
 183    length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
 184    written = write_console_data(event, (uint8_t *)acd->data, length);
 185
 186    rc = SCLP_RC_NORMAL_COMPLETION;
 187    /* set event buffer accepted flag */
 188    evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
 189
 190    /* written will be zero if a pty is not connected - don't treat as error */
 191    if (written < 0) {
 192        /* event buffer not accepted due to error in character layer */
 193        evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
 194        rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
 195    }
 196
 197    return rc;
 198}
 199
 200static const VMStateDescription vmstate_sclpconsole = {
 201    .name = "sclpconsole",
 202    .version_id = 0,
 203    .minimum_version_id = 0,
 204    .fields = (VMStateField[]) {
 205        VMSTATE_BOOL(event.event_pending, SCLPConsole),
 206        VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220),
 207        VMSTATE_UINT32(iov_sclp, SCLPConsole),
 208        VMSTATE_UINT32(iov_bs, SCLPConsole),
 209        VMSTATE_UINT32(iov_data_len, SCLPConsole),
 210        VMSTATE_UINT32(iov_sclp_rest, SCLPConsole),
 211        VMSTATE_END_OF_LIST()
 212     }
 213};
 214
 215/* qemu object creation and initialization functions */
 216
 217/* tell character layer our call-back functions */
 218
 219static int console_init(SCLPEvent *event)
 220{
 221    static bool console_available;
 222
 223    SCLPConsole *scon = SCLP_CONSOLE(event);
 224
 225    if (console_available) {
 226        error_report("Multiple VT220 operator consoles are not supported");
 227        return -1;
 228    }
 229    console_available = true;
 230    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
 231                             chr_read, NULL, NULL, scon, NULL, true);
 232
 233    return 0;
 234}
 235
 236static void console_reset(DeviceState *dev)
 237{
 238   SCLPEvent *event = SCLP_EVENT(dev);
 239   SCLPConsole *scon = SCLP_CONSOLE(event);
 240
 241   event->event_pending = false;
 242   scon->iov_sclp = 0;
 243   scon->iov_bs = 0;
 244   scon->iov_data_len = 0;
 245   scon->iov_sclp_rest = 0;
 246   scon->notify = false;
 247}
 248
 249static Property console_properties[] = {
 250    DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
 251    DEFINE_PROP_END_OF_LIST(),
 252};
 253
 254static void console_class_init(ObjectClass *klass, void *data)
 255{
 256    DeviceClass *dc = DEVICE_CLASS(klass);
 257    SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
 258
 259    dc->props = console_properties;
 260    dc->reset = console_reset;
 261    dc->vmsd = &vmstate_sclpconsole;
 262    ec->init = console_init;
 263    ec->get_send_mask = send_mask;
 264    ec->get_receive_mask = receive_mask;
 265    ec->can_handle_event = can_handle_event;
 266    ec->read_event_data = read_event_data;
 267    ec->write_event_data = write_event_data;
 268    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 269}
 270
 271static const TypeInfo sclp_console_info = {
 272    .name          = "sclpconsole",
 273    .parent        = TYPE_SCLP_EVENT,
 274    .instance_size = sizeof(SCLPConsole),
 275    .class_init    = console_class_init,
 276    .class_size    = sizeof(SCLPEventClass),
 277};
 278
 279static void register_types(void)
 280{
 281    type_register_static(&sclp_console_info);
 282}
 283
 284type_init(register_types)
 285