@@ -3086,6 +3086,13 @@ F: hw/core/clock-vmstate.c
F: hw/core/qdev-clock.c
F: docs/devel/clocks.rst
+Printer Subsystem
+M: Ruien Zhang <zhangruien@bytedance.com>
+S: Maintained
+F: include/printer
+F: printer
+F: qapi/printer.json
+
Usermode Emulation
------------------
Overall usermode emulation
new file mode 100644
@@ -0,0 +1,42 @@
+/*
+ * QEMU Printer subsystem header
+ *
+ * Copyright (c) 2022 ByteDance, Inc.
+ *
+ * Author:
+ * Ruien Zhang <zhangruien@bytedance.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_PRINTER_H
+#define QEMU_PRINTER_H
+
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "qapi/qapi-types-printer.h"
+
+#define TYPE_PRINTERDEV "printerdev"
+
+struct QEMUPrinter {
+ Object *parent_obj;
+
+ char *model;
+ Printerdev *dev;
+
+ QLIST_ENTRY(QEMUPrinter) list;
+};
+
+OBJECT_DECLARE_TYPE(QEMUPrinter, QEMUPrinterClass, PRINTERDEV)
+
+struct QEMUPrinterClass {
+ ObjectClass parent_class;
+};
+
+void qemu_printer_new_from_opts(const char *opt);
+void qemu_printer_del(QEMUPrinter *printer);
+const char *qemu_printer_id(QEMUPrinter *printer);
+QEMUPrinter *qemu_printer_by_id(const char *id);
+
+#endif /* QEMU_PRINTER_H */
@@ -2397,6 +2397,7 @@ genh += hxdep
authz_ss = ss.source_set()
blockdev_ss = ss.source_set()
block_ss = ss.source_set()
+printer_ss = ss.source_set()
chardev_ss = ss.source_set()
common_ss = ss.source_set()
common_user_ss = ss.source_set()
@@ -2455,6 +2456,7 @@ if have_system
'audio',
'backends',
'backends/tpm',
+ 'printer',
'chardev',
'ebpf',
'hw/9pfs',
@@ -2574,6 +2576,7 @@ endif
subdir('audio')
subdir('io')
+subdir('printer')
subdir('chardev')
subdir('fsdev')
subdir('dump')
@@ -2843,6 +2846,13 @@ libqmp = static_library('qmp', qmp_ss.sources() + genh,
qmp = declare_dependency(link_whole: [libqmp])
+printer_ss = printer_ss.apply(config_host, strict: false)
+libprinter = static_library('printer', printer_ss.sources() + genh,
+ name_suffix: 'fa',
+ build_by_default: false)
+
+printer = declare_dependency(link_whole: libprinter)
+
libchardev = static_library('chardev', chardev_ss.sources() + genh,
name_suffix: 'fa',
dependencies: [gnutls],
@@ -2869,7 +2879,7 @@ foreach m : block_mods + softmmu_mods
install_dir: qemu_moddir)
endforeach
-softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp)
+softmmu_ss.add(authz, blockdev, printer, chardev, crypto, io, qmp)
common_ss.add(qom, qemuutil)
common_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: [softmmu_ss])
@@ -208,3 +208,6 @@ option('fdt', type: 'combo', value: 'auto',
option('selinux', type: 'feature', value: 'auto',
description: 'SELinux support in qemu-nbd')
+
+option('printer', type: 'feature', value: 'auto',
+ description: 'Printer subsystem support')
new file mode 100644
@@ -0,0 +1,61 @@
+/*
+ * QEMU Builtin printer backend
+ *
+ * Copyright (c) 2022 ByteDance, Inc.
+ *
+ * Author:
+ * Ruien Zhang <zhangruien@bytedance.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qapi/qapi-visit-printer.h"
+#include "printer/printer.h"
+#include "trace.h"
+
+#define TYPE_PRINTER_BUILTIN TYPE_PRINTERDEV"-builtin"
+
+typedef struct PrinterBuiltin {
+ QEMUPrinter parent;
+
+ void *opaque; /* used by driver itself */
+} PrinterBuiltin;
+
+DECLARE_INSTANCE_CHECKER(PrinterBuiltin, PRINTER_BUILTIN_DEV,
+ TYPE_PRINTER_BUILTIN)
+
+static void printer_builtin_init(Object *obj)
+{
+}
+
+static void printer_builtin_finalize(Object *obj)
+{
+}
+
+static void printer_builtin_class_init(ObjectClass *oc, void *data)
+{
+}
+
+static const TypeInfo printer_builtin_type_info = {
+ .name = TYPE_PRINTER_BUILTIN,
+ .parent = TYPE_PRINTERDEV,
+ .instance_size = sizeof(PrinterBuiltin),
+ .instance_init = printer_builtin_init,
+ .instance_finalize = printer_builtin_finalize,
+ .class_init = printer_builtin_class_init,
+};
+
+static void register_types(void)
+{
+ type_register_static(&printer_builtin_type_info);
+}
+
+type_init(register_types);
new file mode 100644
@@ -0,0 +1,14 @@
+printer_ss.add([files(
+ 'printer.c',
+)])
+
+printer_modules = {}
+foreach m : [
+ ['builtin', files('builtin.c')],
+]
+ module_ss = ss.source_set()
+ module_ss.add(m[1])
+ printer_modules += {m[0] : module_ss}
+endforeach
+
+modules += {'printer': printer_modules}
new file mode 100644
@@ -0,0 +1,191 @@
+/*
+ * QEMU Printer subsystem
+ *
+ * Copyright (c) 2022 ByteDance, Inc.
+ *
+ * Author:
+ * Ruien Zhang <zhangruien@bytedance.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/help_option.h"
+#include "qemu/iov.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/qemu-print.h"
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-printer.h"
+#include "printer/printer.h"
+#include "trace.h"
+
+static QLIST_HEAD(, QEMUPrinter) qemu_printers;
+
+const char *qemu_printer_id(QEMUPrinter *printer)
+{
+ if (printer->dev && printer->dev->id) {
+ return printer->dev->id;
+ }
+
+ return "";
+}
+
+QEMUPrinter *qemu_printer_by_id(const char *id)
+{
+ QEMUPrinter *printer;
+
+ if (!id) {
+ return NULL;
+ }
+
+ QLIST_FOREACH(printer, &qemu_printers, list) {
+ if (!strcmp(qemu_printer_id(printer), id)) {
+ return printer;
+ }
+ }
+
+ return NULL;
+}
+
+static const QEMUPrinterClass *printer_get_class(const char *typename,
+ Error **errp)
+{
+ ObjectClass *oc;
+
+ oc = module_object_class_by_name(typename);
+
+ if (!object_class_dynamic_cast(oc, TYPE_PRINTERDEV)) {
+ error_setg(errp, "%s: missing %s implementation",
+ TYPE_PRINTERDEV, typename);
+ return NULL;
+ }
+
+ if (object_class_is_abstract(oc)) {
+ error_setg(errp, "%s: %s is abstract type", TYPE_PRINTERDEV, typename);
+ return NULL;
+ }
+
+ return PRINTERDEV_CLASS(oc);
+}
+
+static QEMUPrinter *qemu_printer_new(Printerdev *dev, Error **errp)
+{
+ Object *obj;
+ QEMUPrinter *printer = NULL;
+ g_autofree char *typename = NULL;
+ const char *driver = PrinterdevDriver_str(dev->driver);
+
+ typename = g_strdup_printf("%s-%s", TYPE_PRINTERDEV, driver);
+ if (!printer_get_class(typename, errp)) {
+ return NULL;
+ }
+
+ obj = object_new(typename);
+ if (!obj) {
+ return NULL;
+ }
+
+ printer = PRINTERDEV(obj);
+ printer->dev = dev;
+
+ QLIST_INSERT_HEAD(&qemu_printers, printer, list);
+ trace_qemu_printer_new(qemu_printer_id(printer), typename);
+
+ return printer;
+}
+
+typedef struct PrinterdevClassFE {
+ void (*fn)(const char *name, void *opaque);
+ void *opaque;
+} PrinterdevClassFE;
+
+static void printerdev_class_foreach(ObjectClass *klass, void *opaque)
+{
+ PrinterdevClassFE *fe = opaque;
+
+ assert(g_str_has_prefix(object_class_get_name(klass), TYPE_PRINTERDEV"-"));
+ fe->fn(object_class_get_name(klass) + 11, fe->opaque);
+}
+
+static void printerdev_name_foreach(void (*fn)(const char *name, void *opaque),
+ void *opaque)
+{
+ PrinterdevClassFE fe = { .fn = fn, .opaque = opaque };
+
+ object_class_foreach(printerdev_class_foreach, TYPE_PRINTERDEV, false, &fe);
+}
+
+static void help_string_append(const char *name, void *opaque)
+{
+ GString *str = opaque;
+
+ g_string_append_printf(str, "\n %s", name);
+}
+
+void qemu_printer_new_from_opts(const char *opt)
+{
+ Printerdev *dev;
+
+ if (opt && is_help_option(opt)) {
+ GString *str = g_string_new("");
+
+ printerdev_name_foreach(help_string_append, str);
+
+ qemu_printf("Available printerdev backend types: %s\n", str->str);
+ g_string_free(str, true);
+ return;
+ }
+
+ Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal);
+ visit_type_Printerdev(v, NULL, &dev, &error_fatal);
+ visit_free(v);
+
+ if (qemu_printer_by_id(dev->id)) {
+ error_setg(&error_fatal, "%s: id %s already existed",
+ TYPE_PRINTERDEV, dev->id);
+ }
+
+ if (!qemu_printer_new(dev, &error_fatal)) {
+ qapi_free_Printerdev(dev);
+ }
+}
+
+void qemu_printer_del(QEMUPrinter *printer)
+{
+ trace_qemu_printer_del(qemu_printer_id(printer));
+
+ QLIST_REMOVE(printer, list);
+ qapi_free_Printerdev(printer->dev);
+ object_unref(printer);
+}
+
+
+static void printer_init(Object *obj)
+{
+}
+
+static void printer_finalize(Object *obj)
+{
+}
+
+static const TypeInfo printer_type_info = {
+ .name = TYPE_PRINTERDEV,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(QEMUPrinter),
+ .instance_init = printer_init,
+ .instance_finalize = printer_finalize,
+ .abstract = true,
+ .class_size = sizeof(QEMUPrinterClass),
+};
+
+static void register_types(void)
+{
+ type_register_static(&printer_type_info);
+}
+
+type_init(register_types);
new file mode 100644
@@ -0,0 +1,5 @@
+# See docs/devel/tracing.rst for syntax documentation.
+
+# printer.c
+qemu_printer_new(const char *dev, char *typename) "%s: new printer with type %s"
+qemu_printer_del(const char *dev) "%s: delete printer"
new file mode 100644
@@ -0,0 +1 @@
+#include "trace/trace-printer.h"
@@ -59,6 +59,7 @@ if have_system
'rdma',
'rocker',
'tpm',
+ 'printer',
]
endif
if have_system or have_tools
new file mode 100644
@@ -0,0 +1,47 @@
+# -*- mode: python -*-
+#
+# Copyright (C) 2022 Ruien Zhang <zhangruien@bytedance.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+##
+# = Printer
+##
+
+##
+# @PrinterBuiltinOptions:
+#
+# Options of the builtin printer.
+#
+# Since: 6.3
+##
+{ 'struct': 'PrinterBuiltinOptions',
+ 'data': {} }
+
+##
+# @PrinterdevDriver:
+#
+# An enumeration of possible printer backend drivers.
+#
+# Since: 6.3
+##
+{ 'enum': 'PrinterdevDriver',
+ 'data': [ 'builtin' ] }
+
+##
+# @Printerdev:
+#
+# Captures the configuration of a printer device.
+#
+# @id: identifier for monitor commands.
+#
+# Since: 6.3
+##
+{ 'union': 'Printerdev',
+ 'base': {
+ 'id': 'str',
+ 'driver': 'PrinterdevDriver'},
+ 'discriminator': 'driver',
+ 'data': {
+ 'builtin': 'PrinterBuiltinOptions' } }
@@ -93,3 +93,4 @@
{ 'include': 'audio.json' }
{ 'include': 'acpi.json' }
{ 'include': 'pci.json' }
+{ 'include': 'printer.json' }
@@ -3564,6 +3564,14 @@ The available backends are:
traffic identified by a name (preferably a fqdn).
ERST
+DEFHEADING(Printer device options:)
+
+DEF("printerdev", HAS_ARG, QEMU_OPTION_printerdev,
+ "-printerdev help\n"
+ "-printerdev builtin,id=id\n"
+ , QEMU_ARCH_ALL
+)
+
DEFHEADING()
#ifdef CONFIG_TPM
@@ -94,6 +94,7 @@
#ifdef CONFIG_VIRTFS
#include "fsdev/qemu-fsdev.h"
#endif
+#include "printer/printer.h"
#include "sysemu/qtest.h"
#include "disas/disas.h"
@@ -3247,6 +3248,9 @@ void qemu_init(int argc, char **argv, char **envp)
qemu_opt_get(opts, "mount_tag"), &error_abort);
break;
}
+ case QEMU_OPTION_printerdev:
+ qemu_printer_new_from_opts(optarg);
+ break;
case QEMU_OPTION_serial:
add_device_config(DEV_SERIAL, optarg);
default_serial = 0;