From: zhangruien <zhangru...@bytedance.com> This patch describes the skeleton of QEMU printer subsystem with a dummy builtin driver.
Signed-off-by: zhangruien <zhangru...@bytedance.com> --- MAINTAINERS | 7 ++ include/printer/printer.h | 42 ++++++++++ meson.build | 12 ++- meson_options.txt | 3 + printer/builtin.c | 61 +++++++++++++++ printer/meson.build | 14 ++++ printer/printer.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++ printer/trace-events | 5 ++ printer/trace.h | 1 + qapi/meson.build | 1 + qapi/printer.json | 47 ++++++++++++ qapi/qapi-schema.json | 1 + qemu-options.hx | 8 ++ softmmu/vl.c | 4 + 14 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 include/printer/printer.h create mode 100644 printer/builtin.c create mode 100644 printer/meson.build create mode 100644 printer/printer.c create mode 100644 printer/trace-events create mode 100644 printer/trace.h create mode 100644 qapi/printer.json diff --git a/MAINTAINERS b/MAINTAINERS index c98a61caee..689f20d740 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 <zhangru...@bytedance.com> +S: Maintained +F: include/printer +F: printer +F: qapi/printer.json + Usermode Emulation ------------------ Overall usermode emulation diff --git a/include/printer/printer.h b/include/printer/printer.h new file mode 100644 index 0000000000..c8afbc64c8 --- /dev/null +++ b/include/printer/printer.h @@ -0,0 +1,42 @@ +/* + * QEMU Printer subsystem header + * + * Copyright (c) 2022 ByteDance, Inc. + * + * Author: + * Ruien Zhang <zhangru...@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 */ diff --git a/meson.build b/meson.build index c1b1db1e28..b3db26190d 100644 --- a/meson.build +++ b/meson.build @@ -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]) diff --git a/meson_options.txt b/meson_options.txt index 921967eddb..5b3b502798 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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') diff --git a/printer/builtin.c b/printer/builtin.c new file mode 100644 index 0000000000..bc33a1d363 --- /dev/null +++ b/printer/builtin.c @@ -0,0 +1,61 @@ +/* + * QEMU Builtin printer backend + * + * Copyright (c) 2022 ByteDance, Inc. + * + * Author: + * Ruien Zhang <zhangru...@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); diff --git a/printer/meson.build b/printer/meson.build new file mode 100644 index 0000000000..9814de2a57 --- /dev/null +++ b/printer/meson.build @@ -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} diff --git a/printer/printer.c b/printer/printer.c new file mode 100644 index 0000000000..2d3f57a6e1 --- /dev/null +++ b/printer/printer.c @@ -0,0 +1,191 @@ +/* + * QEMU Printer subsystem + * + * Copyright (c) 2022 ByteDance, Inc. + * + * Author: + * Ruien Zhang <zhangru...@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); diff --git a/printer/trace-events b/printer/trace-events new file mode 100644 index 0000000000..e453bbe691 --- /dev/null +++ b/printer/trace-events @@ -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" diff --git a/printer/trace.h b/printer/trace.h new file mode 100644 index 0000000000..9717d37ac7 --- /dev/null +++ b/printer/trace.h @@ -0,0 +1 @@ +#include "trace/trace-printer.h" diff --git a/qapi/meson.build b/qapi/meson.build index c0c49c15e4..f85af6b7d6 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -59,6 +59,7 @@ if have_system 'rdma', 'rocker', 'tpm', + 'printer', ] endif if have_system or have_tools diff --git a/qapi/printer.json b/qapi/printer.json new file mode 100644 index 0000000000..9c2ecfe874 --- /dev/null +++ b/qapi/printer.json @@ -0,0 +1,47 @@ +# -*- mode: python -*- +# +# Copyright (C) 2022 Ruien Zhang <zhangru...@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' } } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 4912b9744e..114b6a80cb 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -93,3 +93,4 @@ { 'include': 'audio.json' } { 'include': 'acpi.json' } { 'include': 'pci.json' } +{ 'include': 'printer.json' } diff --git a/qemu-options.hx b/qemu-options.hx index ec90505d84..448a456f86 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -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 diff --git a/softmmu/vl.c b/softmmu/vl.c index a8cad43691..67b3c48fa1 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -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; -- 2.11.0