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