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