| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * SCLP event types | 
					
						
							|  |  |  |  *    Operations Command - Line Mode input | 
					
						
							|  |  |  |  *    Message            - Line Mode output | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright IBM, Corp. 2013 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: | 
					
						
							|  |  |  |  *  Heinz Graalfs <graalfs@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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:00 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | #include "qemu/thread.h"
 | 
					
						
							|  |  |  | #include "qemu/error-report.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-23 16:35:07 +02:00
										 |  |  | #include "qemu/module.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-26 18:26:44 +04:00
										 |  |  | #include "chardev/char-fe.h"
 | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "hw/s390x/sclp.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:45 +02:00
										 |  |  | #include "migration/vmstate.h"
 | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | #include "hw/s390x/event-facility.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:51 +02:00
										 |  |  | #include "hw/qdev-properties.h"
 | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | #include "hw/s390x/ebcdic.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SIZE_BUFFER 4096
 | 
					
						
							|  |  |  | #define NEWLINE     "\n"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct OprtnsCommand { | 
					
						
							|  |  |  |     EventBufferHeader header; | 
					
						
							|  |  |  |     MDMSU message_unit; | 
					
						
							| 
									
										
										
										
											2020-03-04 16:38:16 +01:00
										 |  |  |     char data[]; | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | } QEMU_PACKED OprtnsCommand; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* max size for line-mode data in 4K SCCB page */ | 
					
						
							|  |  |  | #define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct SCLPConsoleLM { | 
					
						
							|  |  |  |     SCLPEvent event; | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:51 +03:00
										 |  |  |     CharBackend chr; | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |     bool echo;                  /* immediate echo of input if true        */ | 
					
						
							|  |  |  |     uint32_t write_errors;      /* errors writing to char layer           */ | 
					
						
							|  |  |  |     uint32_t length;            /* length of byte stream in buffer        */ | 
					
						
							|  |  |  |     uint8_t buf[SIZE_CONSOLE_BUFFER]; | 
					
						
							|  |  |  | } SCLPConsoleLM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:47:24 +08:00
										 |  |  | #define TYPE_SCLPLM_CONSOLE "sclplmconsole"
 | 
					
						
							|  |  |  | #define SCLPLM_CONSOLE(obj) \
 | 
					
						
							|  |  |  |     OBJECT_CHECK(SCLPConsoleLM, (obj), TYPE_SCLPLM_CONSOLE) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  | *  Character layer call-back functions | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Allow 1 character at a time | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Accumulate bytes from character layer in console buffer, | 
					
						
							|  |  |  |  * event_pending is set when a newline character is encountered | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The maximum command line length is limited by the maximum | 
					
						
							| 
									
										
										
										
											2014-10-29 08:58:05 +00:00
										 |  |  |  * space available in an SCCB. Line mode console input is sent | 
					
						
							|  |  |  |  * truncated to the guest in case it doesn't fit into the SCCB. | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int chr_can_read(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SCLPConsoleLM *scon = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (scon->event.event_pending) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-10-29 08:58:05 +00:00
										 |  |  |     return 1; | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-10 14:19:52 +01:00
										 |  |  | static void chr_read(void *opaque, const uint8_t *buf, int size) | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-10 14:19:52 +01:00
										 |  |  |     SCLPConsoleLM *scon = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |     assert(size == 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (*buf == '\r' || *buf == '\n') { | 
					
						
							|  |  |  |         scon->event.event_pending = true; | 
					
						
							| 
									
										
										
										
											2014-03-10 14:19:52 +01:00
										 |  |  |         sclp_service_interrupt(0); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-10-29 08:58:05 +00:00
										 |  |  |     if (scon->length == SIZE_CONSOLE_BUFFER) { | 
					
						
							|  |  |  |         /* Eat the character, but still process CR and LF.  */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |     scon->buf[scon->length] = *buf; | 
					
						
							|  |  |  |     scon->length += 1; | 
					
						
							|  |  |  |     if (scon->echo) { | 
					
						
							| 
									
										
										
										
											2016-09-06 14:56:04 +01:00
										 |  |  |         /* XXX this blocks entire thread. Rewrite to use
 | 
					
						
							|  |  |  |          * qemu_chr_fe_write and background I/O callbacks */ | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:55 +03:00
										 |  |  |         qemu_chr_fe_write_all(&scon->chr, buf, size); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* functions to be called by event facility */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool can_handle_event(uint8_t type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 18:42:57 +01:00
										 |  |  | static sccb_mask_t send_mask(void) | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 18:42:57 +01:00
										 |  |  | static sccb_mask_t receive_mask(void) | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Triggered by SCLP's read_event_data | 
					
						
							|  |  |  |  * - convert ASCII byte stream to EBCDIC and | 
					
						
							|  |  |  |  * - copy converted data into provided (SCLP) buffer | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, | 
					
						
							|  |  |  |                             int avail) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:47:24 +08:00
										 |  |  |     SCLPConsoleLM *cons = SCLPLM_CONSOLE(event); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     len = cons->length; | 
					
						
							|  |  |  |     /* data need to fit into provided SCLP buffer */ | 
					
						
							|  |  |  |     if (len > avail) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ebcdic_put(buf, (char *)&cons->buf, len); | 
					
						
							|  |  |  |     *size = len; | 
					
						
							|  |  |  |     cons->length = 0; | 
					
						
							|  |  |  |     /* data provided and no more data pending */ | 
					
						
							|  |  |  |     event->event_pending = false; | 
					
						
							| 
									
										
										
										
											2014-10-29 12:52:02 +00:00
										 |  |  |     qemu_notify_event(); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, | 
					
						
							|  |  |  |                            int *slen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int avail, rc; | 
					
						
							|  |  |  |     size_t src_len; | 
					
						
							|  |  |  |     uint8_t *to; | 
					
						
							|  |  |  |     OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!event->event_pending) { | 
					
						
							|  |  |  |         /* no data pending */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     to = (uint8_t *)&oc->data; | 
					
						
							|  |  |  |     avail = *slen - sizeof(OprtnsCommand); | 
					
						
							|  |  |  |     rc = get_console_data(event, to, &src_len, avail); | 
					
						
							|  |  |  |     if (rc) { | 
					
						
							|  |  |  |         /* data didn't fit, try next SCCB */ | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; | 
					
						
							|  |  |  |     oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; | 
					
						
							|  |  |  |     oc->message_unit.cpmsu.length = | 
					
						
							|  |  |  |         cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; | 
					
						
							|  |  |  |     oc->message_unit.text_command.length = | 
					
						
							|  |  |  |         cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; | 
					
						
							|  |  |  |     oc->message_unit.self_def_text_message.length = | 
					
						
							|  |  |  |         cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; | 
					
						
							|  |  |  |     oc->message_unit.text_message.length = | 
					
						
							|  |  |  |         cpu_to_be16(sizeof(GdsSubvector) + src_len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); | 
					
						
							|  |  |  |     oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; | 
					
						
							|  |  |  |     *slen = avail - src_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Triggered by SCLP's write_event_data | 
					
						
							|  |  |  |  *  - write console data to character layer | 
					
						
							|  |  |  |  *  returns < 0 if an error occurred | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:47:24 +08:00
										 |  |  |     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-06 15:08:52 +03:00
										 |  |  |     if (!qemu_chr_fe_backend_connected(&scon->chr)) { | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |         /* If there's no backend, we can just say we consumed all data. */ | 
					
						
							|  |  |  |         return len; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-06 14:56:03 +01:00
										 |  |  |     /* XXX this blocks entire thread. Rewrite to use
 | 
					
						
							|  |  |  |      * qemu_chr_fe_write and background I/O callbacks */ | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:55 +03:00
										 |  |  |     return qemu_chr_fe_write_all(&scon->chr, buf, len); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int process_mdb(SCLPEvent *event, MDBO *mdbo) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int rc; | 
					
						
							|  |  |  |     int len; | 
					
						
							|  |  |  |     uint8_t buffer[SIZE_BUFFER]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     len = be16_to_cpu(mdbo->length); | 
					
						
							|  |  |  |     len -= sizeof(mdbo->length) + sizeof(mdbo->type) | 
					
						
							|  |  |  |             + sizeof(mdbo->mto.line_type_flags) | 
					
						
							|  |  |  |             + sizeof(mdbo->mto.alarm_control) | 
					
						
							|  |  |  |             + sizeof(mdbo->mto._reserved); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(len <= SIZE_BUFFER); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* convert EBCDIC SCLP contents to ASCII console message */ | 
					
						
							|  |  |  |     ascii_put(buffer, mdbo->mto.message, len); | 
					
						
							|  |  |  |     rc = write_console_data(event, (uint8_t *)NEWLINE, 1); | 
					
						
							|  |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         return rc; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return write_console_data(event, buffer, len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int len; | 
					
						
							|  |  |  |     int written; | 
					
						
							|  |  |  |     int errors = 0; | 
					
						
							|  |  |  |     MDBO *mdbo; | 
					
						
							|  |  |  |     SclpMsg *data = (SclpMsg *) ebh; | 
					
						
							| 
									
										
										
										
											2016-03-29 15:47:24 +08:00
										 |  |  |     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     len = be16_to_cpu(data->mdb.header.length); | 
					
						
							|  |  |  |     if (len < sizeof(data->mdb.header)) { | 
					
						
							|  |  |  |         return SCLP_RC_INCONSISTENT_LENGTHS; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     len -= sizeof(data->mdb.header); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* first check message buffers */ | 
					
						
							|  |  |  |     mdbo = data->mdb.mdbo; | 
					
						
							|  |  |  |     while (len > 0) { | 
					
						
							|  |  |  |         if (be16_to_cpu(mdbo->length) > len | 
					
						
							|  |  |  |                 || be16_to_cpu(mdbo->length) == 0) { | 
					
						
							|  |  |  |             return SCLP_RC_INCONSISTENT_LENGTHS; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         len -= be16_to_cpu(mdbo->length); | 
					
						
							|  |  |  |         mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* then execute */ | 
					
						
							|  |  |  |     len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); | 
					
						
							|  |  |  |     mdbo = data->mdb.mdbo; | 
					
						
							|  |  |  |     while (len > 0) { | 
					
						
							|  |  |  |         switch (be16_to_cpu(mdbo->type)) { | 
					
						
							|  |  |  |         case MESSAGE_TEXT: | 
					
						
							|  |  |  |             /* message text object */ | 
					
						
							|  |  |  |             written = process_mdb(event, mdbo); | 
					
						
							|  |  |  |             if (written < 0) { | 
					
						
							|  |  |  |                 /* character layer error */ | 
					
						
							|  |  |  |                 errors++; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: /* ignore */ | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         len -= be16_to_cpu(mdbo->length); | 
					
						
							|  |  |  |         mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (errors) { | 
					
						
							|  |  |  |         scon->write_errors += errors; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return SCLP_RC_NORMAL_COMPLETION; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* functions for live migration */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const VMStateDescription vmstate_sclplmconsole = { | 
					
						
							|  |  |  |     .name = "sclplmconsole", | 
					
						
							|  |  |  |     .version_id = 0, | 
					
						
							|  |  |  |     .minimum_version_id = 0, | 
					
						
							| 
									
										
										
										
											2014-04-16 16:01:33 +02:00
										 |  |  |     .fields = (VMStateField[]) { | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |         VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), | 
					
						
							|  |  |  |         VMSTATE_UINT32(write_errors, SCLPConsoleLM), | 
					
						
							|  |  |  |         VMSTATE_UINT32(length, SCLPConsoleLM), | 
					
						
							|  |  |  |         VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST() | 
					
						
							|  |  |  |      } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* qemu object creation and initialization functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* tell character layer our call-back functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int console_init(SCLPEvent *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static bool console_available; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:47:24 +08:00
										 |  |  |     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (console_available) { | 
					
						
							|  |  |  |         error_report("Multiple line-mode operator consoles are not supported"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     console_available = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 12:52:59 +03:00
										 |  |  |     qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, | 
					
						
							| 
									
										
										
										
											2017-07-06 15:08:49 +03:00
										 |  |  |                              chr_read, NULL, NULL, scon, NULL, true); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void console_reset(DeviceState *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |    SCLPEvent *event = SCLP_EVENT(dev); | 
					
						
							| 
									
										
										
										
											2016-03-29 15:47:24 +08:00
										 |  |  |    SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |    event->event_pending = false; | 
					
						
							|  |  |  |    scon->length = 0; | 
					
						
							|  |  |  |    scon->write_errors = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Property console_properties[] = { | 
					
						
							|  |  |  |     DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), | 
					
						
							|  |  |  |     DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void console_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							|  |  |  |     SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-10 19:30:32 +04:00
										 |  |  |     device_class_set_props(dc, console_properties); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  |     dc->reset = console_reset; | 
					
						
							|  |  |  |     dc->vmsd = &vmstate_sclplmconsole; | 
					
						
							|  |  |  |     ec->init = console_init; | 
					
						
							|  |  |  |     ec->get_send_mask = send_mask; | 
					
						
							|  |  |  |     ec->get_receive_mask = receive_mask; | 
					
						
							|  |  |  |     ec->can_handle_event = can_handle_event; | 
					
						
							|  |  |  |     ec->read_event_data = read_event_data; | 
					
						
							|  |  |  |     ec->write_event_data = write_event_data; | 
					
						
							| 
									
										
										
										
											2015-03-17 13:44:39 +01:00
										 |  |  |     set_bit(DEVICE_CATEGORY_INPUT, dc->categories); | 
					
						
							| 
									
										
										
										
											2013-05-22 14:11:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const TypeInfo sclp_console_info = { | 
					
						
							|  |  |  |     .name          = "sclplmconsole", | 
					
						
							|  |  |  |     .parent        = TYPE_SCLP_EVENT, | 
					
						
							|  |  |  |     .instance_size = sizeof(SCLPConsoleLM), | 
					
						
							|  |  |  |     .class_init    = console_class_init, | 
					
						
							|  |  |  |     .class_size    = sizeof(SCLPEventClass), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void register_types(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     type_register_static(&sclp_console_info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type_init(register_types) |