diff options
Diffstat (limited to 'hw/s390x/3270-ccw.c')
-rw-r--r-- | hw/s390x/3270-ccw.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c new file mode 100644 index 0000000000..a7a5b412e4 --- /dev/null +++ b/hw/s390x/3270-ccw.c @@ -0,0 +1,174 @@ +/* + * Emulated ccw-attached 3270 implementation + * + * Copyright 2017 IBM Corp. + * Author(s): Yang Chen <bjcyang@linux.vnet.ibm.com> + * Jing Liu <liujbjl@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "cpu.h" +#include "hw/s390x/css.h" +#include "hw/s390x/css-bridge.h" +#include "hw/s390x/3270-ccw.h" + +/* Handle READ ccw commands from guest */ +static int handle_payload_3270_read(EmulatedCcw3270Device *dev, CCW1 *ccw) +{ + EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + int len; + + if (!ccw->cda) { + return -EFAULT; + } + + len = ck->read_payload_3270(dev, ccw->cda, ccw->count); + ccw_dev->sch->curr_status.scsw.count = ccw->count - len; + + return 0; +} + +/* Handle WRITE ccw commands to write data to client */ +static int handle_payload_3270_write(EmulatedCcw3270Device *dev, CCW1 *ccw) +{ + EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + int len; + + if (!ccw->cda) { + return -EFAULT; + } + + len = ck->write_payload_3270(dev, ccw->cmd_code, ccw->cda, ccw->count); + + if (len <= 0) { + return -EIO; + } + + ccw_dev->sch->curr_status.scsw.count = ccw->count - len; + return 0; +} + +static int emulated_ccw_3270_cb(SubchDev *sch, CCW1 ccw) +{ + int rc = 0; + EmulatedCcw3270Device *dev = sch->driver_data; + + switch (ccw.cmd_code) { + case TC_WRITESF: + case TC_WRITE: + case TC_EWRITE: + case TC_EWRITEA: + rc = handle_payload_3270_write(dev, &ccw); + break; + case TC_RDBUF: + case TC_READMOD: + rc = handle_payload_3270_read(dev, &ccw); + break; + default: + rc = -ENOSYS; + break; + } + + if (rc == -EIO) { + /* I/O error, specific devices generate specific conditions */ + SCSW *s = &sch->curr_status.scsw; + + sch->curr_status.scsw.dstat = SCSW_DSTAT_UNIT_CHECK; + sch->sense_data[0] = 0x40; /* intervention-req */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + } + + return rc; +} + +static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) +{ + uint16_t chpid; + EmulatedCcw3270Device *dev = EMULATED_CCW_3270(ds); + EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); + CcwDevice *cdev = CCW_DEVICE(ds); + CCWDeviceClass *cdk = CCW_DEVICE_GET_CLASS(cdev); + SubchDev *sch = css_create_virtual_sch(cdev->devno, errp); + Error *err = NULL; + + if (!sch) { + return; + } + + if (!ck->init) { + goto out_err; + } + + sch->driver_data = dev; + cdev->sch = sch; + chpid = css_find_free_chpid(sch->cssid); + + if (chpid > MAX_CHPID) { + error_setg(&err, "No available chpid to use."); + goto out_err; + } + + sch->id.reserved = 0xff; + sch->id.cu_type = EMULATED_CCW_3270_CU_TYPE; + css_sch_build_virtual_schib(sch, (uint8_t)chpid, + EMULATED_CCW_3270_CHPID_TYPE); + sch->ccw_cb = emulated_ccw_3270_cb; + + ck->init(dev, &err); + if (err) { + goto out_err; + } + + cdk->realize(cdev, &err); + if (err) { + goto out_err; + } + + return; + +out_err: + error_propagate(errp, err); + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + cdev->sch = NULL; + g_free(sch); +} + +static Property emulated_ccw_3270_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = emulated_ccw_3270_properties; + dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + dc->realize = emulated_ccw_3270_realize; + dc->hotpluggable = false; +} + +static const TypeInfo emulated_ccw_3270_info = { + .name = TYPE_EMULATED_CCW_3270, + .parent = TYPE_CCW_DEVICE, + .instance_size = sizeof(EmulatedCcw3270Device), + .class_init = emulated_ccw_3270_class_init, + .class_size = sizeof(EmulatedCcw3270Class), + .abstract = true, +}; + +static void emulated_ccw_register(void) +{ + type_register_static(&emulated_ccw_3270_info); +} + +type_init(emulated_ccw_register) |