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