Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fa9da5163 | ||
|
|
4bdf99b6ce | ||
|
|
a8ca829f58 |
16
cmd/mouse.c
16
cmd/mouse.c
@@ -18,13 +18,21 @@ static int do_mouse_dump(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
struct udevice *dev;
|
||||
bool running;
|
||||
int count;
|
||||
int mouse_dev = 0;
|
||||
int ret;
|
||||
|
||||
ret = uclass_first_device_err(UCLASS_MOUSE, &dev);
|
||||
/* Parse optional device number */
|
||||
if (argc > 1)
|
||||
mouse_dev = dectoul(argv[1], NULL);
|
||||
|
||||
/* Get the specified mouse device */
|
||||
ret = uclass_get_device(UCLASS_MOUSE, mouse_dev, &dev);
|
||||
if (ret) {
|
||||
printf("Mouse not found (err=%d)\n", ret);
|
||||
printf("Mouse device %d not found (err=%d)\n", mouse_dev, ret);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
printf("Using mouse device %d: %s\n", mouse_dev, dev->name);
|
||||
for (running = true, count = 0; running;) {
|
||||
struct mouse_event evt;
|
||||
|
||||
@@ -63,7 +71,7 @@ static int do_mouse_dump(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
}
|
||||
|
||||
static char mouse_help_text[] =
|
||||
"dump - Dump input from a mouse";
|
||||
"dump [dev] - Dump input from mouse device (default: 0)";
|
||||
|
||||
U_BOOT_CMD_WITH_SUBCMDS(mouse, "Mouse input", mouse_help_text,
|
||||
U_BOOT_SUBCMD_MKENT(dump, 1, 1, do_mouse_dump));
|
||||
U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_mouse_dump));
|
||||
|
||||
66
doc/usage/cmd/mouse.rst
Normal file
66
doc/usage/cmd/mouse.rst
Normal file
@@ -0,0 +1,66 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0+:
|
||||
|
||||
.. index::
|
||||
single: mouse (command)
|
||||
|
||||
mouse command
|
||||
=============
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
::
|
||||
|
||||
mouse dump [dev]
|
||||
|
||||
The mouse command is used to access mouse input devices.
|
||||
|
||||
mouse dump
|
||||
----------
|
||||
|
||||
Dump input events from a mouse device in real-time. Events are displayed
|
||||
until the user presses Ctrl+C to stop.
|
||||
|
||||
dev
|
||||
Optional device number (default: 0). Use this to select a specific mouse
|
||||
device when multiple mouse devices are available.
|
||||
|
||||
The command displays:
|
||||
- Motion events with absolute/relative coordinates and button state
|
||||
- Button events (left, right, middle) with press/release state and position
|
||||
- Event count when finished
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
::
|
||||
|
||||
=> mouse dump
|
||||
Using mouse device 0: xhci_pci.p0.usb_hub.p6.usb_mo
|
||||
motion: Xrel=-27, Yrel=84, X=0, Y=84, but=0
|
||||
motion: Xrel=-78, Yrel=87, X=0, Y=171, but=0
|
||||
motion: Xrel=-1, Yrel=87, X=0, Y=258, but=0
|
||||
motion: Xrel=76, Yrel=88, X=76, Y=346, but=0
|
||||
button: button==0, press=1, clicks=1, X=76, Y=346
|
||||
motion: Xrel=76, Yrel=88, X=152, Y=434, but=1
|
||||
motion: Xrel=76, Yrel=88, X=228, Y=522, but=1
|
||||
motion: Xrel=76, Yrel=88, X=304, Y=610, but=1
|
||||
motion: Xrel=76, Yrel=88, X=380, Y=698, but=1
|
||||
button: button==0, press=0, clicks=1, X=380, Y=698
|
||||
motion: Xrel=76, Yrel=88, X=456, Y=786, but=0
|
||||
motion: Xrel=76, Yrel=88, X=532, Y=874, but=0
|
||||
motion: Xrel=50, Yrel=88, X=582, Y=962, but=0
|
||||
motion: Xrel=-1, Yrel=87, X=581, Y=1049, but=0
|
||||
motion: Xrel=76, Yrel=87, X=657, Y=1136, but=0
|
||||
motion: Xrel=-104, Yrel=86, X=553, Y=1222, but=0
|
||||
motion: Xrel=24, Yrel=86, X=577, Y=1308, but=0
|
||||
motion: Xrel=-104, Yrel=85, X=473, Y=1393, but=0
|
||||
18 events received
|
||||
|
||||
=> mouse dump 1
|
||||
Mouse device 1 not found (err=-19)
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The mouse command is available when CONFIG_CMD_MOUSE is enabled.
|
||||
@@ -94,6 +94,7 @@ Shell commands
|
||||
cmd/mbr
|
||||
cmd/md
|
||||
cmd/mmc
|
||||
cmd/mouse
|
||||
cmd/msr
|
||||
cmd/mtest
|
||||
cmd/mtrr
|
||||
|
||||
@@ -110,4 +110,17 @@ config VIRTIO_SCSI
|
||||
A specification for the protocol is available at
|
||||
https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html
|
||||
|
||||
config VIRTIO_INPUT
|
||||
bool "Input device driver for virtio devices"
|
||||
depends on VIRTIO
|
||||
select MOUSE
|
||||
default y
|
||||
help
|
||||
This driver provides support for virtio-based input devices such as
|
||||
tablets, mice and keyboards. It implements the mouse uclass for
|
||||
tablet and pointer devices.
|
||||
|
||||
A specification for the protocol is available at
|
||||
https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -14,3 +14,4 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
|
||||
obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
|
||||
obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o fs_compat.o
|
||||
obj-$(CONFIG_VIRTIO_SCSI) += virtio_scsi.o
|
||||
obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
|
||||
|
||||
369
drivers/virtio/virtio_input.c
Normal file
369
drivers/virtio/virtio_input.c
Normal file
@@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* VirtIO Input (tablet) driver for U-Boot
|
||||
*
|
||||
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_VIRTIO
|
||||
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <mouse.h>
|
||||
#include <virtio_types.h>
|
||||
#include <virtio.h>
|
||||
#include <virtio_ring.h>
|
||||
#include <linux/byteorder/little_endian.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
|
||||
#define VIRTIO_INPUT_CFG_UNSET 0x00
|
||||
#define VIRTIO_INPUT_CFG_ID_NAME 0x01
|
||||
#define VIRTIO_INPUT_CFG_ID_SERIAL 0x02
|
||||
#define VIRTIO_INPUT_CFG_ID_DEVIDS 0x03
|
||||
#define VIRTIO_INPUT_CFG_PROP_BITS 0x10
|
||||
#define VIRTIO_INPUT_CFG_EV_BITS 0x11
|
||||
#define VIRTIO_INPUT_CFG_ABS_INFO 0x12
|
||||
|
||||
/* absolute axis information for input devices */
|
||||
struct virtio_input_absinfo {
|
||||
__le32 min; /* minimum value */
|
||||
__le32 max; /* maximum value */
|
||||
__le32 fuzz; /* fuzz value for noise filtering */
|
||||
__le32 flat; /* flat area around center */
|
||||
__le32 res; /* resolution in units per mm */
|
||||
};
|
||||
|
||||
/* device identification information */
|
||||
struct virtio_input_devids {
|
||||
__le16 bustype; /* bus type identifier */
|
||||
__le16 vendor; /* vendor identifier */
|
||||
__le16 product; /* product identifier */
|
||||
__le16 version; /* version number */
|
||||
};
|
||||
|
||||
/* configuration space for querying device capabilities */
|
||||
struct vinp_config {
|
||||
__u8 select; /* configuration item to select */
|
||||
__u8 subsel; /* sub-selection within item */
|
||||
__u8 size; /* size of returned data */
|
||||
__u8 reserved[5]; /* padding */
|
||||
union {
|
||||
char string[128]; /* for name/serial strings */
|
||||
__u8 bitmap[128]; /* for capability bitmaps */
|
||||
struct virtio_input_absinfo abs; /* for absolute axis info */
|
||||
struct virtio_input_devids ids; /* for device IDs */
|
||||
} u;
|
||||
};
|
||||
|
||||
/* input event structure (follows Linux input_event format) */
|
||||
struct virtio_input_event {
|
||||
__le16 type; /* event type (EV_KEY, EV_ABS, EV_SYN, etc.) */
|
||||
__le16 code; /* event code (button/axis identifier) */
|
||||
__le32 value; /* event value */
|
||||
};
|
||||
|
||||
#define BUF_COUNT 8
|
||||
#define BUF_SIZE (16 * sizeof(struct virtio_input_event))
|
||||
#define COORD_MAX 0xffff
|
||||
|
||||
/* private data for virtio input device */
|
||||
struct virtio_input_priv {
|
||||
struct virtqueue *event_vq; /* event virtqueue */
|
||||
struct virtqueue *status_vq; /* status virtqueue */
|
||||
char event_bufs[BUF_COUNT][BUF_SIZE]; /* event buffers */
|
||||
bool rx_running; /* true if buffers are set up */
|
||||
int abs_x_max; /* maximum X coordinate */
|
||||
int abs_y_max; /* maximum Y coordinate */
|
||||
int button_state; /* current button state */
|
||||
int last_x; /* last X coordinate */
|
||||
int last_y; /* last Y coordinate */
|
||||
};
|
||||
|
||||
static int virtio_input_free_buffer(struct udevice *dev, void *buf)
|
||||
{
|
||||
struct virtio_input_priv *priv = dev_get_priv(dev);
|
||||
struct virtio_sg sg = { buf, BUF_SIZE };
|
||||
struct virtio_sg *sgs[] = { &sg };
|
||||
|
||||
/* put the buffer back to the event ring */
|
||||
virtqueue_add(priv->event_vq, sgs, 0, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event(struct virtio_input_priv *priv,
|
||||
struct virtio_input_event *vio_event,
|
||||
struct mouse_event *evt)
|
||||
{
|
||||
u16 type = le16_to_cpu(vio_event->type);
|
||||
u16 code = le16_to_cpu(vio_event->code);
|
||||
u32 value = le32_to_cpu(vio_event->value);
|
||||
|
||||
/* skip EV_SYN events immediately */
|
||||
if (type == EV_SYN)
|
||||
return -EAGAIN;
|
||||
|
||||
log_debug("processing event: type=%d code=%d value=%d\n", type, code,
|
||||
value);
|
||||
|
||||
switch (type) {
|
||||
case EV_ABS:
|
||||
if (code == ABS_X)
|
||||
priv->last_x = value;
|
||||
else if (code == ABS_Y)
|
||||
priv->last_y = value;
|
||||
|
||||
/* report motion event */
|
||||
evt->type = MOUSE_EV_MOTION;
|
||||
evt->motion.state = priv->button_state;
|
||||
evt->motion.x = (priv->last_x * COORD_MAX) / priv->abs_x_max;
|
||||
evt->motion.y = (priv->last_y * COORD_MAX) / priv->abs_y_max;
|
||||
evt->motion.xrel = 0; /* Absolute mode */
|
||||
evt->motion.yrel = 0;
|
||||
return 0;
|
||||
|
||||
case EV_KEY:
|
||||
switch (code) {
|
||||
case BTN_LEFT:
|
||||
evt->type = MOUSE_EV_BUTTON;
|
||||
evt->button.button = BUTTON_LEFT;
|
||||
evt->button.press_state = value ? BUTTON_PRESSED :
|
||||
BUTTON_RELEASED;
|
||||
evt->button.clicks = 1;
|
||||
evt->button.x = (priv->last_x * COORD_MAX) /
|
||||
priv->abs_x_max;
|
||||
evt->button.y = (priv->last_y * COORD_MAX) /
|
||||
priv->abs_y_max;
|
||||
|
||||
if (evt->button.press_state == BUTTON_PRESSED)
|
||||
priv->button_state |= BUTTON_LEFT;
|
||||
else
|
||||
priv->button_state &= ~BUTTON_LEFT;
|
||||
return 0;
|
||||
case BTN_RIGHT:
|
||||
evt->type = MOUSE_EV_BUTTON;
|
||||
evt->button.button = BUTTON_RIGHT;
|
||||
evt->button.press_state = value ? BUTTON_PRESSED :
|
||||
BUTTON_RELEASED;
|
||||
evt->button.clicks = 1;
|
||||
evt->button.x = (priv->last_x * COORD_MAX) /
|
||||
priv->abs_x_max;
|
||||
evt->button.y = (priv->last_y * COORD_MAX) /
|
||||
priv->abs_y_max;
|
||||
|
||||
if (evt->button.press_state == BUTTON_PRESSED)
|
||||
priv->button_state |= BUTTON_RIGHT;
|
||||
else
|
||||
priv->button_state &= ~BUTTON_RIGHT;
|
||||
return 0;
|
||||
case BTN_MIDDLE:
|
||||
evt->type = MOUSE_EV_BUTTON;
|
||||
evt->button.button = BUTTON_MIDDLE;
|
||||
evt->button.press_state = value ? BUTTON_PRESSED :
|
||||
BUTTON_RELEASED;
|
||||
evt->button.clicks = 1;
|
||||
evt->button.x = (priv->last_x * COORD_MAX) /
|
||||
priv->abs_x_max;
|
||||
evt->button.y = (priv->last_y * COORD_MAX) /
|
||||
priv->abs_y_max;
|
||||
|
||||
if (evt->button.press_state == BUTTON_PRESSED)
|
||||
priv->button_state |= BUTTON_MIDDLE;
|
||||
else
|
||||
priv->button_state &= ~BUTTON_MIDDLE;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int virtio_input_start(struct udevice *dev)
|
||||
{
|
||||
struct virtio_input_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (!priv->rx_running) {
|
||||
struct virtio_sg sg;
|
||||
struct virtio_sg *sgs[] = { &sg };
|
||||
int i;
|
||||
|
||||
/* setup event buffers */
|
||||
sg.length = BUF_SIZE;
|
||||
|
||||
/* add all buffers to the event queue */
|
||||
for (i = 0; i < BUF_COUNT; i++) {
|
||||
sg.addr = priv->event_bufs[i];
|
||||
virtqueue_add(priv->event_vq, sgs, 0, 1);
|
||||
}
|
||||
|
||||
virtqueue_kick(priv->event_vq);
|
||||
|
||||
/* setup the event queue only once */
|
||||
priv->rx_running = true;
|
||||
log_debug("VirtIO input buffers initialized\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_input_get_event(struct udevice *dev, struct mouse_event *evt)
|
||||
{
|
||||
struct virtio_input_priv *priv = dev_get_priv(dev);
|
||||
struct virtio_input_event *vio_event;
|
||||
int i, num_events, ret;
|
||||
void *buf;
|
||||
uint len;
|
||||
|
||||
/* ensure buffers are setup */
|
||||
ret = virtio_input_start(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
log_debug("starting\n");
|
||||
|
||||
/* check for ready buffer */
|
||||
buf = virtqueue_get_buf(priv->event_vq, &len);
|
||||
if (!buf) {
|
||||
log_debug("No events available\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
log_debug("got buffer back: addr=%p len=%d\n", buf, len);
|
||||
if (len < sizeof(struct virtio_input_event)) {
|
||||
log_debug("Invalid event length: %d\n", len);
|
||||
/* put buffer back */
|
||||
virtio_input_free_buffer(dev, buf);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* process all events in the buffer */
|
||||
num_events = len / sizeof(struct virtio_input_event);
|
||||
vio_event = (struct virtio_input_event *)buf;
|
||||
|
||||
for (i = 0; i < num_events; i++) {
|
||||
ret = process_event(priv, &vio_event[i], evt);
|
||||
if (!ret) {
|
||||
/*
|
||||
* found a valid event to return, put buffer back
|
||||
* Note: this loses any remaining events in the buffer,
|
||||
* but for input devices this is acceptable
|
||||
*/
|
||||
virtio_input_free_buffer(dev, buf);
|
||||
return 0;
|
||||
}
|
||||
/* -EAGAIN means continue to next event */
|
||||
}
|
||||
|
||||
/* no useful events found, put buffer back */
|
||||
virtio_input_free_buffer(dev, buf);
|
||||
evt->type = MOUSE_EV_NULL;
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int virtio_input_probe(struct udevice *dev)
|
||||
{
|
||||
struct virtio_input_priv *priv = dev_get_priv(dev);
|
||||
struct virtqueue *vqs[2];
|
||||
struct vinp_config cfg;
|
||||
int ret;
|
||||
|
||||
ret = virtio_find_vqs(dev, 2, vqs);
|
||||
if (ret) {
|
||||
log_debug("Failed to find virtqueues: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->event_vq = vqs[0];
|
||||
priv->status_vq = vqs[1];
|
||||
|
||||
/* check what event types this device supports */
|
||||
cfg.select = VIRTIO_INPUT_CFG_EV_BITS;
|
||||
cfg.subsel = EV_KEY;
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, select), cfg.select);
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel), cfg.subsel);
|
||||
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.bitmap),
|
||||
cfg.u.bitmap, 16);
|
||||
log_debug("EV_KEY bitmap: %02x %02x %02x %02x\n", cfg.u.bitmap[0],
|
||||
cfg.u.bitmap[1], cfg.u.bitmap[2], cfg.u.bitmap[3]);
|
||||
|
||||
cfg.select = VIRTIO_INPUT_CFG_EV_BITS;
|
||||
cfg.subsel = EV_REL;
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, select), cfg.select);
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel), cfg.subsel);
|
||||
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.bitmap),
|
||||
cfg.u.bitmap, 16);
|
||||
log_debug("EV_REL bitmap: %02x %02x %02x %02x\n", cfg.u.bitmap[0],
|
||||
cfg.u.bitmap[1], cfg.u.bitmap[2], cfg.u.bitmap[3]);
|
||||
|
||||
/* check if this device supports absolute coordinates (tablet) */
|
||||
cfg.select = VIRTIO_INPUT_CFG_EV_BITS;
|
||||
cfg.subsel = EV_ABS;
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, select), cfg.select);
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel), cfg.subsel);
|
||||
|
||||
/* read the bitmap to see if ABS_X and ABS_Y are supported */
|
||||
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.bitmap),
|
||||
cfg.u.bitmap, 16);
|
||||
|
||||
log_debug("EV_ABS bitmap: %02x %02x %02x %02x\n", cfg.u.bitmap[0],
|
||||
cfg.u.bitmap[1], cfg.u.bitmap[2], cfg.u.bitmap[3]);
|
||||
|
||||
/* check if ABS_X (0) and ABS_Y (1) bits are set */
|
||||
if ((cfg.u.bitmap[0] & (1 << 0)) &&
|
||||
(cfg.u.bitmap[0] & (1 << 1))) {
|
||||
/* get ABS_X range */
|
||||
cfg.select = VIRTIO_INPUT_CFG_ABS_INFO;
|
||||
cfg.subsel = ABS_X;
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, select),
|
||||
cfg.select);
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel),
|
||||
cfg.subsel);
|
||||
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.abs),
|
||||
&cfg.u.abs, sizeof(cfg.u.abs));
|
||||
priv->abs_x_max = le32_to_cpu(cfg.u.abs.max);
|
||||
|
||||
/* get ABS_Y range */
|
||||
cfg.subsel = ABS_Y;
|
||||
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel),
|
||||
cfg.subsel);
|
||||
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.abs),
|
||||
&cfg.u.abs, sizeof(cfg.u.abs));
|
||||
priv->abs_y_max = le32_to_cpu(cfg.u.abs.max);
|
||||
|
||||
log_debug("tablet: X range 0-%d, Y range 0-%d\n",
|
||||
priv->abs_x_max, priv->abs_y_max);
|
||||
} else {
|
||||
/* no absolute coordinates, use default range */
|
||||
priv->abs_x_max = 32767;
|
||||
priv->abs_y_max = 32767;
|
||||
log_debug("table: No absolute coords, using relative mode\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_input_bind(struct udevice *dev)
|
||||
{
|
||||
struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
|
||||
|
||||
/* indicate what driver features we support */
|
||||
virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mouse_ops virtio_input_ops = {
|
||||
.get_event = virtio_input_get_event,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(virtio_input) = {
|
||||
.name = VIRTIO_INPUT_DRV_NAME,
|
||||
.id = UCLASS_MOUSE,
|
||||
.bind = virtio_input_bind,
|
||||
.probe = virtio_input_probe,
|
||||
.ops = &virtio_input_ops,
|
||||
.priv_auto = sizeof(struct virtio_input_priv),
|
||||
.flags = DM_FLAG_ACTIVE_DMA,
|
||||
};
|
||||
@@ -278,7 +278,8 @@ class BuildQemu:
|
||||
qemu_cmd.extend(['-display', 'default,show-cursor=on'])
|
||||
elif self.args.arch == 'x86':
|
||||
qemu_cmd.extend(['-device', 'qemu-xhci'])
|
||||
qemu_cmd.extend(['-device', 'usb-kbd', '-device', 'usb-tablet'])
|
||||
qemu_cmd.extend(['-device', 'usb-kbd'])
|
||||
qemu_cmd.extend(['-device', 'virtio-tablet-pci'])
|
||||
qemu_cmd.extend(['-display', 'default,show-cursor=on'])
|
||||
if not any(item.startswith('-serial') for item in self.qemu_extra):
|
||||
qemu_cmd.extend(['-serial', 'mon:stdio'])
|
||||
|
||||
Reference in New Issue
Block a user