All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Introduce printer subsystem and USB printer device
@ 2022-01-13 11:56 Ruien Zhang
  2022-01-13 11:56 ` [PATCH 1/2] printer: Introduce printer subsystem Ruien Zhang
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Ruien Zhang @ 2022-01-13 11:56 UTC (permalink / raw)
  To: peter.maydell, richard.henderson, kraxel, eblake, pbonzini, berrange
  Cc: qemu-devel, zhangruien

From: zhangruien <zhangruien@bytedance.com>

Currently, printer support in QEMU can generally be considered with these
approaches:

1) USB passthrough & redirection, with the limitation of flexibility and
   transport-specific issues that come along with.

2) Network reachability with network printers, which is also driver-specific,
   thus less friendly to small systems.

Driverless Printing [1] may or may not be network-dependent, the former is the
general case while the latter imposes less restraints on cloud environments, and
it doesn't necessarily mean that we have to follow the methods in 1) to achieve
this. Transport protocols targeted at devices such as USB printer class [2] with
the extension of IPP-over-USB [3] and many others can be integrated into QEMU,
presenting more flexibility and functionality.

This patchset introduces:

1) Skeleton of QEMU printer subsystem with a dummy builtin driver.

2) USB printer device emulation, with definitions in the extension of IPP-over-
   USB [3].

WIP:

1) QEMU printer subsystem interfaces, which will be finalized with a concrete
   backend driver.

2) IPP-over-USB implementation.

[1]: https://openprinting.github.io/driverless
[2]: https://www.usb.org/sites/default/files/usbprint11a021811.pdf
[3]: https://www.usb.org/document-library/ipp-protocol-10

zhangruien (2):
  printer: Introduce printer subsystem
  usb-printer: Introduce USB printer class

 MAINTAINERS                 |   7 +
 docs/system/devices/usb.rst |   3 +
 hw/usb/Kconfig              |   5 +
 hw/usb/dev-printer.c        | 423 ++++++++++++++++++++++++++++++++++++++++++++
 hw/usb/meson.build          |   1 +
 hw/usb/trace-events         |  11 ++
 include/hw/usb/printer.h    |  93 ++++++++++
 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 +
 20 files changed, 932 insertions(+), 1 deletion(-)
 create mode 100644 hw/usb/dev-printer.c
 create mode 100644 include/hw/usb/printer.h
 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

-- 
2.11.0



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] printer: Introduce printer subsystem
  2022-01-13 11:56 [PATCH 0/2] Introduce printer subsystem and USB printer device Ruien Zhang
@ 2022-01-13 11:56 ` Ruien Zhang
  2022-01-13 11:56 ` [PATCH 2/2] usb-printer: Introduce USB printer class Ruien Zhang
  2022-01-14  9:32 ` [PATCH 0/2] Introduce printer subsystem and USB printer device Gerd Hoffmann
  2 siblings, 0 replies; 7+ messages in thread
From: Ruien Zhang @ 2022-01-13 11:56 UTC (permalink / raw)
  To: peter.maydell, richard.henderson, kraxel, eblake, pbonzini, berrange
  Cc: qemu-devel, zhangruien

From: zhangruien <zhangruien@bytedance.com>

This patch describes the skeleton of QEMU printer subsystem with a
dummy builtin driver.

Signed-off-by: zhangruien <zhangruien@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 <zhangruien@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 <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 */
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 <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);
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 <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);
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 <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' } }
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



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] usb-printer: Introduce USB printer class
  2022-01-13 11:56 [PATCH 0/2] Introduce printer subsystem and USB printer device Ruien Zhang
  2022-01-13 11:56 ` [PATCH 1/2] printer: Introduce printer subsystem Ruien Zhang
@ 2022-01-13 11:56 ` Ruien Zhang
  2022-01-14  9:32 ` [PATCH 0/2] Introduce printer subsystem and USB printer device Gerd Hoffmann
  2 siblings, 0 replies; 7+ messages in thread
From: Ruien Zhang @ 2022-01-13 11:56 UTC (permalink / raw)
  To: peter.maydell, richard.henderson, kraxel, eblake, pbonzini, berrange
  Cc: qemu-devel, zhangruien

From: zhangruien <zhangruien@bytedance.com>

The USB printer device emulation is currently provided with:

1) Definitions and corresponding action handlers of class-specific
   requests with essential descriptors in USB Printer Class
   Specification 1.1 [1].

2) Extended definitions of interface protocol and class-specific
   descriptors in IPP-over-USB protocol 1.0 [2].

A usb printer device can be assembled with the following example of
command-line arguments:

    -device piix4-usb-uhci,id=uhci,bus=pci.0 \
    -device usb-printer,id=usb-printer0,printerdev=printer0,bus=uhci.0,terminal=printer \
    -printerdev builtin,id=printer0

[1]: https://www.usb.org/sites/default/files/usbprint11a021811.pdf
[2]: https://www.usb.org/document-library/ipp-protocol-10

Signed-off-by: zhangruien <zhangruien@bytedance.com>
---
 docs/system/devices/usb.rst |   3 +
 hw/usb/Kconfig              |   5 +
 hw/usb/dev-printer.c        | 423 ++++++++++++++++++++++++++++++++++++++++++++
 hw/usb/meson.build          |   1 +
 hw/usb/trace-events         |  11 ++
 include/hw/usb/printer.h    |  93 ++++++++++
 6 files changed, 536 insertions(+)
 create mode 100644 hw/usb/dev-printer.c
 create mode 100644 include/hw/usb/printer.h

diff --git a/docs/system/devices/usb.rst b/docs/system/devices/usb.rst
index afb7d6c226..6e87c3be11 100644
--- a/docs/system/devices/usb.rst
+++ b/docs/system/devices/usb.rst
@@ -199,6 +199,9 @@ option or the ``device_add`` monitor command. Available devices are:
 ``u2f-{emulated,passthru}``
    Universal Second Factor device
 
+``usb-printer``
+   USB printer device
+
 Physical port addressing
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
index 53f8283ffd..1b5a953cae 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -133,3 +133,8 @@ config XLNX_USB_SUBSYS
     bool
     default y if XLNX_VERSAL
     select USB_DWC3
+
+config USB_PRINTER
+    bool
+    default y
+    depends on USB
diff --git a/hw/usb/dev-printer.c b/hw/usb/dev-printer.c
new file mode 100644
index 0000000000..5905615961
--- /dev/null
+++ b/hw/usb/dev-printer.c
@@ -0,0 +1,423 @@
+/*
+ * USB Printer Device emulation
+ *
+ * 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.
+ */
+
+/*
+ * References:
+ *   Universal Serial Bus Device Class Definition for Printing Devices,
+ *   version 1.1
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "hw/usb.h"
+#include "hw/usb/printer.h"
+#include "printer/printer.h"
+#include "desc.h"
+#include "trace.h"
+
+#define USBPRINTER_VENDOR_NUM     0x46f4 /* CRC16() of "QEMU" */
+#define USBPRINTER_PRODUCT_NUM    0xa1f3
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT,
+    STR_SERIALNUMBER,
+    STR_CONFIG_FULL,
+    STR_CONFIG_HIGH,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER] = "QEMU",
+    [STR_PRODUCT]      = "QEMU USB Printer",
+    [STR_SERIALNUMBER] = "1",
+    [STR_CONFIG_FULL]  = "Full speed config (usb 1.1)",
+    [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
+};
+
+/*
+ * 5. Standard Descriptors
+ *
+ * "Printer Class devices support the following standard USB descriptors:
+ *  - Device. Each printer has one device descriptor.
+ *  - Configuration. Each device has one default configuration descriptor which
+ *    supports at least one interface.
+ *  - Interface. A printer device has a single data interface with possible
+ *    alternates.
+ *  - Endpoint. A printer device supports the following endpoints:
+ *  - Bulk OUT endpoint. Used for transfer of PDL/PCP data.
+ *  - Optional Bulk IN endpoint. Provides status and other return information."
+ */
+static const USBDescIface desc_iface_full = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = EP_NUMS_2,
+    .bInterfaceClass               = USB_CLASS_PRINTER,
+    .bInterfaceSubClass            = SC_PRINTERS,
+    .bInterfaceProtocol            = PC_PROTOCOL_BIDIR_1284_4,
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_OUT | EP_NUM_BULK_OUT,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },{
+            .bEndpointAddress      = USB_DIR_IN | EP_NUM_BULK_IN,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 64,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_full = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_FULL,
+            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
+            .nif = 1,
+            .ifs = &desc_iface_full,
+        },
+    },
+};
+
+static const USBDescIface desc_iface_high = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = EP_NUMS_2,
+    .bInterfaceClass               = USB_CLASS_PRINTER,
+    .bInterfaceSubClass            = SC_PRINTERS,
+    .bInterfaceProtocol            = PC_PROTOCOL_BIDIR_1284_4,
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_OUT | EP_NUM_BULK_OUT,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 512,
+        },{
+            .bEndpointAddress      = USB_DIR_IN | EP_NUM_BULK_IN,
+            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize        = 512,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_high = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_HIGH,
+            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
+            .nif = 1,
+            .ifs = &desc_iface_high,
+        },
+    },
+};
+
+static const USBDesc desc_printer = {
+    .id = {
+        .idVendor          = USB_CLASS_PRINTER,
+        .idProduct         = USBPRINTER_PRODUCT_NUM,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full  = &desc_device_full,
+    .high  = &desc_device_high,
+    .str   = desc_strings,
+};
+
+struct USBPrinterState {
+    /* qemu interfaces */
+    USBDevice dev;
+
+    /* state */
+    QEMUPrinter *printer;
+
+    /* properties */
+    char *printerdev;
+    char *terminal;
+};
+
+#define TYPE_USB_PRINTER "usb-printer"
+OBJECT_DECLARE_SIMPLE_TYPE(USBPrinterState, USB_PRINTER)
+
+static void usb_printer_handle_reset(USBDevice *dev)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    trace_usb_printer_handle_reset(bus->busnr, dev->addr);
+}
+
+/*
+ * 4.2.1 GET_DEVICE_ID (bRequest = 0)
+ * "This class-specific request returns a device ID string that is compatible
+ *  with IEEE 1284. See IEEE 1284 for syntax and formatting information."
+ */
+#define USB_PRINTER_DEVICE_ID_QEMU "QEMU Printer"
+#define USB_PRINTER_DEVICE_ID_QEMU_LEN \
+    strlen(USB_PRINTER_DEVICE_ID_QEMU)
+#define USB_PRINTER_DEVICE_ID_QEMU_LEN_IEEE_1284 \
+    (2 + USB_PRINTER_DEVICE_ID_QEMU_LEN)
+
+static const USBPrinterDeviceIDStrings usb_printer_device_ids = {
+    [USB_PRINTER_DEVICE_ID_DEFAULT] = USB_PRINTER_DEVICE_ID_QEMU,
+};
+
+static int usb_printer_get_device_id(USBDevice *dev, int request, int value,
+                                 int index, int length, uint8_t *data)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+
+    *((uint16_t *)data) = cpu_to_be16(USB_PRINTER_DEVICE_ID_QEMU_LEN_IEEE_1284);
+    memcpy(data + 2, usb_printer_device_ids[USB_PRINTER_DEVICE_ID_DEFAULT],
+        USB_PRINTER_DEVICE_ID_QEMU_LEN);
+
+    trace_usb_printer_get_device_id(bus->busnr, dev->addr);
+
+    return USB_PRINTER_DEVICE_ID_QEMU_LEN_IEEE_1284;
+}
+
+/*
+ * 4.2.2 GET_PORT_STATUS (bRequest = 1)
+ *
+ * "Note: Some USB printers may not always be able to determine this
+ *  information. In this case, they should return benign status of
+ *  “Paper Not Empty,” “Selected,” and “No Error.”"
+ */
+static int usb_printer_get_port_status(USBDevice *dev, int request, int value,
+                                 int index, int length, uint8_t *data)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+
+    *((uint8_t *)data) = PAPER_NOT_EMPTY | SELECTED | NO_ERROR;
+    trace_usb_printer_get_port_status(bus->busnr, dev->addr);
+    return 1;
+}
+
+/*
+ * TODO: 4.2.3 SOFT_RESET (bRequest = 2)
+ *
+ * "This class-specific request flushes all buffers and resets the Bulk OUT
+ *  and Bulk IN pipes to their default states. This request clears all stall
+ *  conditions. This reset does NOT change the USB addressing or USB
+ *  configuration."
+ */
+static int usb_printer_handle_soft_reset(USBDevice *dev, int request, int value,
+                                 int index, int length, uint8_t *data)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+
+    trace_usb_printer_handle_soft_reset(bus->busnr, dev->addr);
+    return 0;
+}
+
+static void usb_printer_handle_control(USBDevice *dev, USBPacket *p,
+                                    int request, int value, int index,
+                                    int length, uint8_t *data)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    int ret = 0;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return;
+    }
+
+    switch (request) {
+    case ClassInterfaceRequest | USBPRINTER_GET_DEVICE_ID:
+        ret = usb_printer_get_device_id(dev, request, value, index,
+                                        length, data);
+        if (ret < 0) {
+            goto error;
+        }
+        break;
+
+    case ClassInterfaceRequest | USBPRINTER_GET_PORT_STATUS:
+        ret = usb_printer_get_port_status(dev, request, value, index,
+                                          length, data);
+        if (ret < 0) {
+            goto error;
+        }
+        break;
+
+    case ClassInterfaceOutRequestCompat1_0 | USBPRINTER_SOFT_RESET:
+        /* fall through */
+    case ClassInterfaceOutRequest | USBPRINTER_SOFT_RESET:
+        ret = usb_printer_handle_soft_reset(dev, request, value, index,
+                                            length, data);
+        if (ret < 0) {
+            goto error;
+        }
+        break;
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "%s: request %x not implemented\n",
+                      TYPE_USB_PRINTER, request);
+        goto error;
+    }
+
+    p->actual_length = ret;
+    p->status = USB_RET_SUCCESS;
+    return;
+
+error:
+    trace_usb_printer_handle_control_error(bus->busnr, dev->addr, request,
+        value, index, length);
+    p->status = USB_RET_STALL;
+}
+
+static void usb_printer_handle_data_out(USBDevice *dev, USBPacket *p)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
+
+    p->status = USB_RET_SUCCESS;
+    trace_usb_printer_handle_data_out(bus->busnr, dev->addr, iov->size);
+}
+
+/*
+ * 5.4.2 Bulk IN Endpoint
+ *
+ * "The Bulk IN endpoint is used to return any data generated by the PDL
+ *  or PCP to the host. If the printer supports a PCP, such as IEEE-1284.1
+ *  or IEEE-1284.4, this endpoint will return status or other printer-related
+ *  information."
+ */
+static void usb_printer_handle_data_in(USBDevice *dev, USBPacket *p)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
+
+    p->status = USB_RET_SUCCESS;
+    trace_usb_printer_handle_data_in(bus->busnr, dev->addr, iov->size);
+}
+
+static void usb_printer_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        switch (p->ep->nr) {
+        case EP_NUM_BULK_OUT:
+            usb_printer_handle_data_out(dev, p);
+            return;
+
+        default:
+            goto fail;
+        }
+        break;
+
+    case USB_TOKEN_IN:
+        switch (p->ep->nr) {
+        case EP_NUM_BULK_IN:
+            usb_printer_handle_data_in(dev, p);
+            return;
+
+        default:
+            goto fail;
+        }
+        break;
+
+    default:
+    fail:
+        p->status = USB_RET_STALL;
+        break;
+    }
+
+    if (p->status == USB_RET_STALL) {
+        fprintf(stderr, "usbprinter: failed data transaction: "
+                        "pid 0x%x ep 0x%x len 0x%zx\n",
+                        p->pid, p->ep->nr, p->iov.size);
+    }
+
+    trace_usb_printer_handle_data(bus->busnr, dev->addr, p->pid, p->ep->nr);
+}
+
+static void usb_printer_unrealize(USBDevice *dev)
+{
+}
+
+static void usb_printer_realize(USBDevice *dev, Error **errp)
+{
+    USBPrinterState *s = USB_PRINTER(dev);
+    if (!s->terminal || strcmp(s->terminal, "printer")) {
+        error_setg(errp, "%s: support terminal printer only", TYPE_USB_PRINTER);
+        return;
+    }
+
+    s->printer = qemu_printer_by_id(s->printerdev);
+    if (!s->printer) {
+        error_setg(errp, "%s: invalid printerdev %s",
+                   TYPE_USB_PRINTER, s->printerdev);
+        return;
+    }
+
+    dev->usb_desc = &desc_printer;
+
+    usb_desc_create_serial(dev);
+    usb_desc_init(dev);
+    s->dev.opaque = s;
+}
+
+/* TODO: set alternates on IPP-over-USB */
+static void usb_printer_set_interface(USBDevice *dev, int iface,
+                                    int old, int value)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    trace_usb_printer_set_interface(bus->busnr, dev->addr, iface, old, value);
+}
+
+static Property usb_printer_properties[] = {
+    DEFINE_PROP_STRING("printerdev", USBPrinterState, printerdev),
+    DEFINE_PROP_STRING("terminal", USBPrinterState, terminal),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_printer_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *k = USB_DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, usb_printer_properties);
+    set_bit(DEVICE_CATEGORY_USB, dc->categories);
+    k->product_desc   = "QEMU USB Printer Interface";
+    k->realize        = usb_printer_realize;
+    k->handle_reset   = usb_printer_handle_reset;
+    k->handle_control = usb_printer_handle_control;
+    k->handle_data    = usb_printer_handle_data;
+    k->unrealize      = usb_printer_unrealize;
+    k->set_interface = usb_printer_set_interface;
+}
+
+static const TypeInfo usb_printer_info = {
+    .name          = TYPE_USB_PRINTER,
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBPrinterState),
+    .class_init    = usb_printer_class_init,
+};
+
+static void usb_printer_register_types(void)
+{
+    type_register_static(&usb_printer_info);
+}
+
+type_init(usb_printer_register_types)
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index de853d780d..f79d5e1f74 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -44,6 +44,7 @@ softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c'))
 softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c'))
 softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c'))
 softmmu_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c'))
+softmmu_ss.add(when: 'CONFIG_USB_PRINTER', if_true: files('dev-printer.c'))
 softmmu_ss.add(when: ['CONFIG_POSIX', 'CONFIG_USB_STORAGE_MTP'], if_true: files('dev-mtp.c'))
 
 # smartcard
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
index b8287b63f1..e3fed30c43 100644
--- a/hw/usb/trace-events
+++ b/hw/usb/trace-events
@@ -345,3 +345,14 @@ usb_serial_set_baud(int bus, int addr, int baud) "dev %d:%u baud rate %d"
 usb_serial_set_data(int bus, int addr, int parity, int data, int stop) "dev %d:%u parity %c, data bits %d, stop bits %d"
 usb_serial_set_flow_control(int bus, int addr, int index) "dev %d:%u flow control %d"
 usb_serial_set_xonxoff(int bus, int addr, uint8_t xon, uint8_t xoff) "dev %d:%u xon 0x%x xoff 0x%x"
+
+# dev-printer.c
+usb_printer_handle_reset(int bus, int addr) "dev %d:%u reset"
+usb_printer_get_device_id(int bus, int addr) "dev %d:%u get device id"
+usb_printer_get_port_status(int bus, int addr) "dev %d:%u get port status"
+usb_printer_handle_soft_reset(int bus, int addr) "dev %d:%u soft reset"
+usb_printer_handle_control_error(int bus, int addr, int request, int value, int index, int length) "dev %d:%u handle control error, request 0x%x, value 0x%x, index 0x%x, length 0x%x"
+usb_printer_handle_data(int bus, int addr, int pid, int ep) "dev %d:%u data, pid 0x%x, ep %d"
+usb_printer_handle_data_out(int bus, int addr, int size) "dev %d:%u data out, size %d"
+usb_printer_handle_data_in(int bus, int addr, int size) "dev %d:%u data in, size %d"
+usb_printer_set_interface(int bus, int addr, int iface, int old, int value) "dev %d:%u set interface %d, old %d, value %d"
diff --git a/include/hw/usb/printer.h b/include/hw/usb/printer.h
new file mode 100644
index 0000000000..0b14e11b8f
--- /dev/null
+++ b/include/hw/usb/printer.h
@@ -0,0 +1,93 @@
+/*
+ * USB Printer Device emulation
+ *
+ * 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.
+ */
+
+/*
+ * References:
+ *   Universal Serial Bus Device Class Definition for Printing Devices,
+ *   version 1.1
+ *   USB Print Interface Class IPP Protocol Specification, revision 1.0
+ */
+
+#ifndef HW_USB_PRINTER_H
+#define HW_USB_PRINTER_H
+
+/* 4.2 Class-Specific Requests */
+#define USBPRINTER_GET_DEVICE_ID   0
+#define USBPRINTER_GET_PORT_STATUS 1
+#define USBPRINTER_SOFT_RESET      2
+
+typedef enum {
+    USB_PRINTER_DEVICE_ID_DEFAULT,
+    USB_PRINTER_DEVICE_ID_MAX
+} USBPrinterDeviceIDType;
+
+typedef const char *USBPrinterDeviceIDStrings[USB_PRINTER_DEVICE_ID_MAX];
+
+/* 4.2.2 GET_PORT_STATUS (bRequest = 1) */
+#define PAPER_EMPTY     (1 << 5)
+#define PAPER_NOT_EMPTY (0 << 5)
+#define SELECTED        (1 << 4)
+#define NOT_SELECTED    (0 << 4)
+#define NO_ERROR        (1 << 3)
+#define ERROR           (0 << 3)
+
+/*
+ * 4.2.3 SOFT_RESET (bRequest = 2)
+ *
+ * "Note: Version 1.0 of the specification incorrectly stated that the
+ *  bmReqestType for SOFT_RESET was 00100011B. Version 1.1 Host software
+ *  implementers should be prepared for USB printers that expect this
+ *  request code, and version 1.1 device implementers should be prepared
+ *  for host software that issues this request code."
+ */
+#define ClassInterfaceOutRequestCompat1_0 \
+        ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER) << 8)
+
+/* 5.3 Interface Descriptors */
+#define EP_NUMS_1                0x01
+#define EP_NUMS_2                0x02
+#define EP_NUM_BULK_OUT          0x01
+#define EP_NUM_BULK_IN           0x02
+#define SC_PRINTERS              0x01
+#define PC_PROTOCOL_UNIDIR       0x01
+#define PC_PROTOCOL_BIDIR        0x02
+#define PC_PROTOCOL_BIDIR_1284_4 0x03
+#define PC_PROTOCOL_IPP_USB      0x04
+#define PC_VENDOR_SPECIFIC       0xff
+
+/* 4.3 Device Info Descriptor: A Class Specific Descriptor */
+#define DEV_INFO_DESC_CHECK_LEN(bLength) \
+        QEMU_BUILD_BUG_ON((bLength) < 10)
+
+#define DEV_INFO_DESC_CHECK_NUM_DESCS(bNumDescriptors) \
+        QEMU_BUILD_BUG_ON((bNumDescriptors) < 1)
+
+#define DEV_INFO_DESC_CHECK_OPT_CT(bCapabilitiesType) \
+        QEMU_BUILD_BUG_ON((bCapabilitiesType) < 0x20 || \
+                          (bCapabilitiesType) > 0xff)
+
+#define IPP_USB_CT_BASIC    0x00
+
+#define IPP_USB_CAP_BASIC_PRINT                 (1 << 0)
+#define IPP_USB_CAP_BASIC_SCAN                  (1 << 1)
+#define IPP_USB_CAP_BASIC_FAX                   (1 << 2)
+#define IPP_USB_CAP_BASIC_OTHER                 (1 << 3)
+#define IPP_USB_CAP_BASIC_ANY_HTTP_1_1_OVER_USB (1 << 4)
+
+#define IPP_USB_CAP_BASIC_AUTH_NONE              (0x00 << 5)
+#define IPP_USB_CAP_BASIC_AUTH_USERNAME_PASSWORD (0x01 << 5)
+#define IPP_USB_CAP_BASIC_AUTH_RESERVED          (0x02 << 5)
+#define IPP_USB_CAP_BASIC_AUTH_NEGOTIATE         (0x03 << 5)
+
+/* TODO: IPP string table in IPP server implementation */
+
+#endif /* HW_USB_PRINTER_H */
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Introduce printer subsystem and USB printer device
  2022-01-13 11:56 [PATCH 0/2] Introduce printer subsystem and USB printer device Ruien Zhang
  2022-01-13 11:56 ` [PATCH 1/2] printer: Introduce printer subsystem Ruien Zhang
  2022-01-13 11:56 ` [PATCH 2/2] usb-printer: Introduce USB printer class Ruien Zhang
@ 2022-01-14  9:32 ` Gerd Hoffmann
  2022-01-14 13:38   ` Ruien Zhang
  2022-01-15 15:31   ` Ruien Zhang
  2 siblings, 2 replies; 7+ messages in thread
From: Gerd Hoffmann @ 2022-01-14  9:32 UTC (permalink / raw)
  To: Ruien Zhang
  Cc: peter.maydell, berrange, richard.henderson, qemu-devel, pbonzini, eblake

  Hi,

> This patchset introduces:
> 
> 1) Skeleton of QEMU printer subsystem with a dummy builtin driver.
> 
> 2) USB printer device emulation, with definitions in the extension of IPP-over-
>    USB [3].
> 
> WIP:
> 
> 1) QEMU printer subsystem interfaces, which will be finalized with a concrete
>    backend driver.
> 
> 2) IPP-over-USB implementation.

Hmm, I'm wondering what uses cases you have in mind and whenever
it makes sense to introduce a printer subsystem?

Having an ipp-over-usb device looks useful, but the only use case I can
see is to allow guests access a network printer.  I can't see the
benefits of a printer subsystem, especially in a world where non-ipp
printers are going extinct.  We would most likely have just a single
kind of printer backend, where the only job qemu will have is to
forwarding requests and replies, maybe with some http header rewriting.

Likewise usb would be the one and only device (parallel ports are long
gone in printers).  So the indirection added by a printer subsystem
doesn't buy us anything because we just don't need that flexibility.
I'd suggest to pass the url directly to the device instead:

qemu -device usb-ipp-printer,url=ipp://hostname/ipp/printer

take care,
  Gerd



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Introduce printer subsystem and USB printer device
  2022-01-14  9:32 ` [PATCH 0/2] Introduce printer subsystem and USB printer device Gerd Hoffmann
@ 2022-01-14 13:38   ` Ruien Zhang
  2022-01-15 15:31   ` Ruien Zhang
  1 sibling, 0 replies; 7+ messages in thread
From: Ruien Zhang @ 2022-01-14 13:38 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: peter.maydell, berrange, richard.henderson, qemu-devel, pbonzini, eblake

On 1/14/22 5:32 PM, Gerd Hoffmann wrote:
>    Hi,
> 
>> This patchset introduces:
>>
>> 1) Skeleton of QEMU printer subsystem with a dummy builtin driver.
>>
>> 2) USB printer device emulation, with definitions in the extension of IPP-over-
>>     USB [3].
>>
>> WIP:
>>
>> 1) QEMU printer subsystem interfaces, which will be finalized with a concrete
>>     backend driver.
>>
>> 2) IPP-over-USB implementation.
> 
> Hmm, I'm wondering what uses cases you have in mind and whenever
> it makes sense to introduce a printer subsystem?
> 

Simply for the "potential" backend diversity. I have to admit that I 
haven't figured out another backend which would be commonly-seen either, 
which is also one part of the reason why the interfaces are not firming 
up right now.

> Having an ipp-over-usb device looks useful, but the only use case I can
> see is to allow guests access a network printer.  I can't see the
> benefits of a printer subsystem, especially in a world where non-ipp
> printers are going extinct.  We would most likely have just a single
> kind of printer backend, where the only job qemu will have is to
> forwarding requests and replies, maybe with some http header rewriting.
> 
> Likewise usb would be the one and only device (parallel ports are long
> gone in printers).  So the indirection added by a printer subsystem
> doesn't buy us anything because we just don't need that flexibility.
> I'd suggest to pass the url directly to the device instead:
> 
> qemu -device usb-ipp-printer,url=ipp://hostname/ipp/printer
> 
> take care,
>    Gerd
> 

Indeed, the subsystem is an over-abstraction. The forwarding way is much 
neater, considering how things really work nowadays.

Anyway, thanks for the practical suggestion, it will be revised, along 
with other designs around the path I'm currently working on.

Regards,
Ruien




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Introduce printer subsystem and USB printer device
  2022-01-14  9:32 ` [PATCH 0/2] Introduce printer subsystem and USB printer device Gerd Hoffmann
  2022-01-14 13:38   ` Ruien Zhang
@ 2022-01-15 15:31   ` Ruien Zhang
  2022-01-17  7:17     ` Gerd Hoffmann
  1 sibling, 1 reply; 7+ messages in thread
From: Ruien Zhang @ 2022-01-15 15:31 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: peter.maydell, berrange, richard.henderson, qemu-devel, pbonzini, eblake

On 1/14/22 5:32 PM, Gerd Hoffmann wrote:
>    Hi,
> 
>> This patchset introduces:
>>
>> 1) Skeleton of QEMU printer subsystem with a dummy builtin driver.
>>
>> 2) USB printer device emulation, with definitions in the extension of IPP-over-
>>     USB [3].
>>
>> WIP:
>>
>> 1) QEMU printer subsystem interfaces, which will be finalized with a concrete
>>     backend driver.
>>
>> 2) IPP-over-USB implementation.
> 
> Hmm, I'm wondering what uses cases you have in mind and whenever
> it makes sense to introduce a printer subsystem?

I'm having an idea about the use case, let's discuss a bit more about it 
here.

If I want to expose some Virtual Device Interfaces (VDI) on USB-IPP 
printer device to remote desktop service like spice-server, is it 
rational to register these interfaces to the printer subsystem which 
will play as a middle layer? A concrete example is QXL virtual GPU with 
VDI between QEMU and spice-server. What if other QEMU-emulated devices 
also want to take part in the spice service routine? This can be 
achieved naturally by the registered handlers in the subsystems, which 
further exchange data with the underlying devices (USB-IPP printer here, 
for example). Nevertheless, I totally agree that it is straightforward 
to make devices like USB-IPP printer to be passed-through in local 
environments, which prompts me to add the uri-option. But I want it to 
be compatible with other use cases, like this one.

I'll give this idea a try.

> 
> Having an ipp-over-usb device looks useful, but the only use case I can
> see is to allow guests access a network printer.  I can't see the
> benefits of a printer subsystem, especially in a world where non-ipp
> printers are going extinct.  We would most likely have just a single
> kind of printer backend, where the only job qemu will have is to
> forwarding requests and replies, maybe with some http header rewriting.
> 
> Likewise usb would be the one and only device (parallel ports are long
> gone in printers).  So the indirection added by a printer subsystem
> doesn't buy us anything because we just don't need that flexibility.
> I'd suggest to pass the url directly to the device instead:
> 
> qemu -device usb-ipp-printer,url=ipp://hostname/ipp/printer
> 
> take care,
>    Gerd
> 
Regards,
Ruien



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Introduce printer subsystem and USB printer device
  2022-01-15 15:31   ` Ruien Zhang
@ 2022-01-17  7:17     ` Gerd Hoffmann
  0 siblings, 0 replies; 7+ messages in thread
From: Gerd Hoffmann @ 2022-01-17  7:17 UTC (permalink / raw)
  To: Ruien Zhang
  Cc: peter.maydell, berrange, richard.henderson, qemu-devel, pbonzini, eblake

  Hi,

> If I want to expose some Virtual Device Interfaces (VDI) on USB-IPP printer
> device to remote desktop service like spice-server, is it rational to
> register these interfaces to the printer subsystem which will play as a
> middle layer?

I'd suggest to implement the usb printer in the spice client then and
connect it to the virtual machine using the usb redirection protocol,
similar to the existing usb storage emulation for cdrom redirection.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-01-17  7:34 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-13 11:56 [PATCH 0/2] Introduce printer subsystem and USB printer device Ruien Zhang
2022-01-13 11:56 ` [PATCH 1/2] printer: Introduce printer subsystem Ruien Zhang
2022-01-13 11:56 ` [PATCH 2/2] usb-printer: Introduce USB printer class Ruien Zhang
2022-01-14  9:32 ` [PATCH 0/2] Introduce printer subsystem and USB printer device Gerd Hoffmann
2022-01-14 13:38   ` Ruien Zhang
2022-01-15 15:31   ` Ruien Zhang
2022-01-17  7:17     ` Gerd Hoffmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.