From nobody Wed Apr 24 23:37:28 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=bytedance.com ARC-Seal: i=1; a=rsa-sha256; t=1642085823; cv=none; d=zohomail.com; s=zohoarc; b=CHIVldypEFv2Fc036XKzRE/iR1zPke2Uo6cSbzzpvyoK32nWzKxIW1iURMJ3osHGBGG3PqKdGIQ0oxWe//WJ9/GKn1M81d+HpDztVZmJO9tgye/u4XHeVEmhMPjowUZWgrTIPbi4upDWNgslz/uxUXosN19IbKLUSuyA9BIk0e4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1642085823; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=cltHoKovWAaw2o+jBjnNyo83LuXjlr4IX9eikMJ8vMk=; b=dBIdOfA9Pq8/981ibBAMRevFAtIF7OkZsoMxnIhLsCYvDKDZ2S4iUxb3u0x/kYerLCAMwJCk03hiM7683RfDW21wxyNmlnMlOcKp7gdu6s9t33+oXdWDL5Hro+T8BQNT0zbRdDX8Cf9ivbbiWDCn7BvEPL/F/xIaoUbeDR6EsdM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1642085823753943.6007101385886; Thu, 13 Jan 2022 06:57:03 -0800 (PST) Received: from localhost ([::1]:57452 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1n81XC-0005Tx-5O for importer@patchew.org; Thu, 13 Jan 2022 09:57:02 -0500 Received: from eggs.gnu.org ([209.51.188.92]:37546) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1n7yok-0006pE-RX for qemu-devel@nongnu.org; Thu, 13 Jan 2022 07:02:59 -0500 Received: from [2607:f8b0:4864:20::f36] (port=45985 helo=mail-qv1-xf36.google.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1n7yoh-0006As-44 for qemu-devel@nongnu.org; Thu, 13 Jan 2022 07:02:57 -0500 Received: by mail-qv1-xf36.google.com with SMTP id 15so6304013qvp.12 for ; Thu, 13 Jan 2022 04:02:48 -0800 (PST) Received: from localhost.localdomain ([139.177.225.242]) by smtp.gmail.com with ESMTPSA id f9sm2387572pjh.18.2022.01.13.03.57.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 13 Jan 2022 03:57:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bytedance-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cltHoKovWAaw2o+jBjnNyo83LuXjlr4IX9eikMJ8vMk=; b=WYgEH6poLfTO/bJooR4x0MYPzfEdI6ryViojIkxcY8U8Lxrj4d/NaMSejV7uuNxzjo IXtuu5GDHnk90ZTp1ttbSLWFYLzQy/E0GL3SeQdbLZuQIOOPst0Qm3ybo4apDxvQiP3e PCJ1rOEHftZm2sy/vpkZUNyWzyL6AH7GDHArToLitfn5cJ88dOTSifGQw28Cmw2Oh4Pg Wd+L+/byDRIueiKD51QhJEgj6SFORDrFoSYtOMiJLNmuAAUCNrfqll6zmWGw2aOhGGZY wkFtGdVquXFYWkyZ2CLsogS+c4mZbF/WOo3CmjUJC29M4f4c23r84MLLZLN88ytYd9zy MASw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=cltHoKovWAaw2o+jBjnNyo83LuXjlr4IX9eikMJ8vMk=; b=OT4i4piSA6wJ2QYtxIJco1AEsl8dFA6YlzbZQiJN5oxfkSgByAtiNas/+AlxJS5WK5 ZInND4HaivaH1tfYcFbOVqkl9f7ey8e6hRSv4fOO8PtGmEWSVfotCNr3Va4XqTAos/nW w7wxVrJ0sXVh3yEhf/EXTqM0NSreQs+TSd32rBO+shavqlB/sDEpDZm7oK+apYBbxrrB hCYixeYBHpKdTWfKRy0gT/ajAavxxU0zNyq+s5SprOCgtj51uYlGaHZBzqOMOkoC3U1/ /sZqtaq1ibN4zOqs+gSjaK/Z1lB5AKpWJZkVaXOL/0qWrLbTG1Cu0m+sVwIKr4QUHAjU FHOw== X-Gm-Message-State: AOAM532kn2uj79rq1JHn4aqwp7Dgf3bVvN8RQq2/Qc5iFxOFTcoln+nH 0uClHyzCM7sI//3fJqu4fe3fGww6UWNIgA== X-Google-Smtp-Source: ABdhPJz4wUzkIpcVsYmB5ygNsd2h9yRdEpF9Ju4wR86H8MvhMChlqXokgOlCWQyU6fp1RihPakIwPA== X-Received: by 2002:a17:902:bc86:b0:149:8dd5:a382 with SMTP id bb6-20020a170902bc8600b001498dd5a382mr4088144plb.52.1642075031396; Thu, 13 Jan 2022 03:57:11 -0800 (PST) From: Ruien Zhang To: peter.maydell@linaro.org, richard.henderson@linaro.org, kraxel@redhat.com, eblake@redhat.com, pbonzini@redhat.com, berrange@redhat.com Subject: [PATCH 1/2] printer: Introduce printer subsystem Date: Thu, 13 Jan 2022 19:56:58 +0800 Message-Id: <20220113115659.72788-2-zhangruien@bytedance.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20220113115659.72788-1-zhangruien@bytedance.com> References: <20220113115659.72788-1-zhangruien@bytedance.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 2607:f8b0:4864:20::f36 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::f36; envelope-from=zhangruien@bytedance.com; helo=mail-qv1-xf36.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 13 Jan 2022 09:54:20 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, zhangruien Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @bytedance-com.20210112.gappssmtp.com) X-ZM-MESSAGEID: 1642085825446100001 Content-Type: text/plain; charset="utf-8" From: zhangruien This patch describes the skeleton of QEMU printer subsystem with a dummy builtin driver. Signed-off-by: zhangruien --- 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 =20 +Printer Subsystem +M: Ruien Zhang +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 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * 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 +=3D hxdep authz_ss =3D ss.source_set() blockdev_ss =3D ss.source_set() block_ss =3D ss.source_set() +printer_ss =3D ss.source_set() chardev_ss =3D ss.source_set() common_ss =3D ss.source_set() common_user_ss =3D ss.source_set() @@ -2455,6 +2456,7 @@ if have_system 'audio', 'backends', 'backends/tpm', + 'printer', 'chardev', 'ebpf', 'hw/9pfs', @@ -2574,6 +2576,7 @@ endif =20 subdir('audio') subdir('io') +subdir('printer') subdir('chardev') subdir('fsdev') subdir('dump') @@ -2843,6 +2846,13 @@ libqmp =3D static_library('qmp', qmp_ss.sources() + = genh, =20 qmp =3D declare_dependency(link_whole: [libqmp]) =20 +printer_ss =3D printer_ss.apply(config_host, strict: false) +libprinter =3D static_library('printer', printer_ss.sources() + genh, + name_suffix: 'fa', + build_by_default: false) + +printer =3D declare_dependency(link_whole: libprinter) + libchardev =3D 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 =20 -softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp) +softmmu_ss.add(authz, blockdev, printer, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) =20 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', =20 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 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * 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 =3D { + .name =3D TYPE_PRINTER_BUILTIN, + .parent =3D TYPE_PRINTERDEV, + .instance_size =3D sizeof(PrinterBuiltin), + .instance_init =3D printer_builtin_init, + .instance_finalize =3D printer_builtin_finalize, + .class_init =3D 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 =3D {} +foreach m : [ + ['builtin', files('builtin.c')], +] + module_ss =3D ss.source_set() + module_ss.add(m[1]) + printer_modules +=3D {m[0] : module_ss} +endforeach + +modules +=3D {'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 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * 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 =3D 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, typen= ame); + return NULL; + } + + return PRINTERDEV_CLASS(oc); +} + +static QEMUPrinter *qemu_printer_new(Printerdev *dev, Error **errp) +{ + Object *obj; + QEMUPrinter *printer =3D NULL; + g_autofree char *typename =3D NULL; + const char *driver =3D PrinterdevDriver_str(dev->driver); + + typename =3D g_strdup_printf("%s-%s", TYPE_PRINTERDEV, driver); + if (!printer_get_class(typename, errp)) { + return NULL; + } + + obj =3D object_new(typename); + if (!obj) { + return NULL; + } + + printer =3D PRINTERDEV(obj); + printer->dev =3D 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 =3D 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 *opa= que), + void *opaque) +{ + PrinterdevClassFE fe =3D { .fn =3D fn, .opaque =3D opaque }; + + object_class_foreach(printerdev_class_foreach, TYPE_PRINTERDEV, false,= &fe); +} + +static void help_string_append(const char *name, void *opaque) +{ + GString *str =3D 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 =3D 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 =3D qobject_input_visitor_new_str(opt, "driver", &error_fat= al); + 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 =3D { + .name =3D TYPE_PRINTERDEV, + .parent =3D TYPE_OBJECT, + .instance_size =3D sizeof(QEMUPrinter), + .instance_init =3D printer_init, + .instance_finalize =3D printer_finalize, + .abstract =3D true, + .class_size =3D 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 ty= pe %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 +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# =3D 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 =20 +DEFHEADING(Printer device options:) + +DEF("printerdev", HAS_ARG, QEMU_OPTION_printerdev, + "-printerdev help\n" + "-printerdev builtin,id=3Did\n" + , QEMU_ARCH_ALL +) + DEFHEADING() =20 #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" =20 #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 =3D 0; --=20 2.11.0 From nobody Wed Apr 24 23:37:28 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=bytedance.com ARC-Seal: i=1; a=rsa-sha256; t=1642085842; cv=none; d=zohomail.com; s=zohoarc; b=ecsI1BYGjxMk1TITDTz/gH1I3eoafAXoiy5o6ZJZZ//qxUIn2lfq2E1fNUpc5jH4As7njezrJMbnxaYcB+w54JzSm0eUA+kyTnSogX6aqmdJL9elSGO/x9rPG8NzbuYbVtoUvVSmeGVTkOToEygwj/7J0+ybh6jUgZoB9kosH/4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1642085842; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=2xjLG3dwedW0tcslzlKmmg7MecP9prVqxPm3ZByDAhQ=; b=XAPgP28H6HTpfU/3g5LYFYdi8xUXHY/eVwP43I7k0b8NQMoCJ1uQ8NN9XrPKP4VOHQyQfybDlGpDDB37yB9PoOMc4gDfc65ssOG95FkQalqHm9hCSW/FU/ihQfAE2lpVKI0lEgbz5b9N+0Ph/APcp0OdfntcQY5T4z5vEE1nTPM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1642085842041102.39716458941291; Thu, 13 Jan 2022 06:57:22 -0800 (PST) Received: from localhost ([::1]:57768 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1n81XU-0005hj-WC for importer@patchew.org; Thu, 13 Jan 2022 09:57:21 -0500 Received: from eggs.gnu.org ([209.51.188.92]:36506) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1n7yjS-0001JL-El for qemu-devel@nongnu.org; Thu, 13 Jan 2022 06:57:30 -0500 Received: from [2607:f8b0:4864:20::644] (port=38717 helo=mail-pl1-x644.google.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1n7yjL-0005Ov-9E for qemu-devel@nongnu.org; Thu, 13 Jan 2022 06:57:30 -0500 Received: by mail-pl1-x644.google.com with SMTP id c3so9383768pls.5 for ; Thu, 13 Jan 2022 03:57:16 -0800 (PST) Received: from localhost.localdomain ([139.177.225.242]) by smtp.gmail.com with ESMTPSA id f9sm2387572pjh.18.2022.01.13.03.57.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 13 Jan 2022 03:57:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bytedance-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=2xjLG3dwedW0tcslzlKmmg7MecP9prVqxPm3ZByDAhQ=; b=iUTR8PxPoKJDcTGVcJIDSCH8P80Q8i3fbXewCphnvrewIeu1ljzv1VHG3OUz2u93y6 +TyXGtefrnfLXzMi8gXMThVqJ5sHOauMRTOtMdRlNFT+WH1zJn3CmyQkadPSdBFZmgqg YgR1ZJVRRFEBcgHziXATxvY3a6Em5LoNHnP/r4xtOO0/tCGwfWC5QDUe83durE2AUC47 G8WSAGqkOxrkk3f8IsZe8LOkV610M9nI9Hvd50QPpkZkngTEsMObPDNLBw+ja3/rADJs 8kjytbvITsYBdD1ndllE2O5Dhc22nE23U4z6HFN1J2L5dG8FGX4T/mCR4+lSqpFK55rY SL8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=2xjLG3dwedW0tcslzlKmmg7MecP9prVqxPm3ZByDAhQ=; b=YJ5wEkJV/hLaVhYVtxqRbiCmVCocY6gabWYl1mYDwF7P9OtfbK4lh9e2maza2QFMpE htIHU2wsevvPXpCLAHqM7UnOOSDjx+9m4rJOuS8A07odzV2hcA2IIjfEfWgOd5EuPfBA 0wYRhLmdhEamV+vAhSHVdsd/Gg9ehwQ2X/8yGw2UV36md5OBkYtr2XB6/NoYFJ+nOGN7 tl1ehgfEo9Zlt77zQtiHL1pcw5bSnM6bk4AeH/ALDM/hxU9kwTvDhm/il4KZDv0FB32n IkLRvjsNqI8VCZ+HihyzWpkOAnYOI6HUZwiz2WKsr6lPUivt8wWeI7Xxn/CjFiNEPBu8 jOrg== X-Gm-Message-State: AOAM533wkA3Jt0g5lzrOUH9JrIbKBh1asyAbN6erAJ3x9oQobPUiyMkp uarXz4foPeNWYG+B7JbFRepfvQ== X-Google-Smtp-Source: ABdhPJyE4VssxVtNeMc7AJ77JzvW4PfG1CjBGu8VaGWH1INfVeOhMB9amrm9IwtJRpjvZYvZ03F25g== X-Received: by 2002:a17:902:7588:b0:14a:3006:ea64 with SMTP id j8-20020a170902758800b0014a3006ea64mr4363476pll.168.1642075035331; Thu, 13 Jan 2022 03:57:15 -0800 (PST) From: Ruien Zhang To: peter.maydell@linaro.org, richard.henderson@linaro.org, kraxel@redhat.com, eblake@redhat.com, pbonzini@redhat.com, berrange@redhat.com Subject: [PATCH 2/2] usb-printer: Introduce USB printer class Date: Thu, 13 Jan 2022 19:56:59 +0800 Message-Id: <20220113115659.72788-3-zhangruien@bytedance.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20220113115659.72788-1-zhangruien@bytedance.com> References: <20220113115659.72788-1-zhangruien@bytedance.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 2607:f8b0:4864:20::644 (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::644; envelope-from=zhangruien@bytedance.com; helo=mail-pl1-x644.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 13 Jan 2022 09:54:20 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, zhangruien Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @bytedance-com.20210112.gappssmtp.com) X-ZM-MESSAGEID: 1642085843550100001 From: zhangruien 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=3Duhci,bus=3Dpci.0 \ -device usb-printer,id=3Dusb-printer0,printerdev=3Dprinter0,bus=3Duhci.= 0,terminal=3Dprinter \ -printerdev builtin,id=3Dprinter0 [1]: https://www.usb.org/sites/default/files/usbprint11a021811.pdf [2]: https://www.usb.org/document-library/ipp-protocol-10 Signed-off-by: zhangruien --- 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 =20 +``usb-printer`` + USB printer device + Physical port addressing ^^^^^^^^^^^^^^^^^^^^^^^^ =20 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 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * 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 =3D 1, + STR_PRODUCT, + STR_SERIALNUMBER, + STR_CONFIG_FULL, + STR_CONFIG_HIGH, +}; + +static const USBDescStrings desc_strings =3D { + [STR_MANUFACTURER] =3D "QEMU", + [STR_PRODUCT] =3D "QEMU USB Printer", + [STR_SERIALNUMBER] =3D "1", + [STR_CONFIG_FULL] =3D "Full speed config (usb 1.1)", + [STR_CONFIG_HIGH] =3D "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 informat= ion." + */ +static const USBDescIface desc_iface_full =3D { + .bInterfaceNumber =3D 0, + .bNumEndpoints =3D EP_NUMS_2, + .bInterfaceClass =3D USB_CLASS_PRINTER, + .bInterfaceSubClass =3D SC_PRINTERS, + .bInterfaceProtocol =3D PC_PROTOCOL_BIDIR_1284_4, + .eps =3D (USBDescEndpoint[]) { + { + .bEndpointAddress =3D USB_DIR_OUT | EP_NUM_BULK_OUT, + .bmAttributes =3D USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize =3D 64, + },{ + .bEndpointAddress =3D USB_DIR_IN | EP_NUM_BULK_IN, + .bmAttributes =3D USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize =3D 64, + }, + }, +}; + +static const USBDescDevice desc_device_full =3D { + .bcdUSB =3D 0x0200, + .bMaxPacketSize0 =3D 8, + .bNumConfigurations =3D 1, + .confs =3D (USBDescConfig[]) { + { + .bNumInterfaces =3D 1, + .bConfigurationValue =3D 1, + .iConfiguration =3D STR_CONFIG_FULL, + .bmAttributes =3D USB_CFG_ATT_ONE | USB_CFG_ATT_SELFP= OWER, + .nif =3D 1, + .ifs =3D &desc_iface_full, + }, + }, +}; + +static const USBDescIface desc_iface_high =3D { + .bInterfaceNumber =3D 0, + .bNumEndpoints =3D EP_NUMS_2, + .bInterfaceClass =3D USB_CLASS_PRINTER, + .bInterfaceSubClass =3D SC_PRINTERS, + .bInterfaceProtocol =3D PC_PROTOCOL_BIDIR_1284_4, + .eps =3D (USBDescEndpoint[]) { + { + .bEndpointAddress =3D USB_DIR_OUT | EP_NUM_BULK_OUT, + .bmAttributes =3D USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize =3D 512, + },{ + .bEndpointAddress =3D USB_DIR_IN | EP_NUM_BULK_IN, + .bmAttributes =3D USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize =3D 512, + }, + }, +}; + +static const USBDescDevice desc_device_high =3D { + .bcdUSB =3D 0x0200, + .bMaxPacketSize0 =3D 64, + .bNumConfigurations =3D 1, + .confs =3D (USBDescConfig[]) { + { + .bNumInterfaces =3D 1, + .bConfigurationValue =3D 1, + .iConfiguration =3D STR_CONFIG_HIGH, + .bmAttributes =3D USB_CFG_ATT_ONE | USB_CFG_ATT_SELFP= OWER, + .nif =3D 1, + .ifs =3D &desc_iface_high, + }, + }, +}; + +static const USBDesc desc_printer =3D { + .id =3D { + .idVendor =3D USB_CLASS_PRINTER, + .idProduct =3D USBPRINTER_PRODUCT_NUM, + .bcdDevice =3D 0, + .iManufacturer =3D STR_MANUFACTURER, + .iProduct =3D STR_PRODUCT, + .iSerialNumber =3D STR_SERIALNUMBER, + }, + .full =3D &desc_device_full, + .high =3D &desc_device_high, + .str =3D 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 =3D usb_bus_from_device(dev); + trace_usb_printer_handle_reset(bus->busnr, dev->addr); +} + +/* + * 4.2.1 GET_DEVICE_ID (bRequest =3D 0) + * "This class-specific request returns a device ID string that is compati= ble + * 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 =3D { + [USB_PRINTER_DEVICE_ID_DEFAULT] =3D USB_PRINTER_DEVICE_ID_QEMU, +}; + +static int usb_printer_get_device_id(USBDevice *dev, int request, int valu= e, + int index, int length, uint8_t *data) +{ + USBBus *bus =3D usb_bus_from_device(dev); + + *((uint16_t *)data) =3D cpu_to_be16(USB_PRINTER_DEVICE_ID_QEMU_LEN_IEE= E_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 =3D 1) + * + * "Note: Some USB printers may not always be able to determine this + * information. In this case, they should return benign status of + * =E2=80=9CPaper Not Empty,=E2=80=9D =E2=80=9CSelected,=E2=80=9D and =E2= =80=9CNo Error.=E2=80=9D" + */ +static int usb_printer_get_port_status(USBDevice *dev, int request, int va= lue, + int index, int length, uint8_t *data) +{ + USBBus *bus =3D usb_bus_from_device(dev); + + *((uint8_t *)data) =3D 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 =3D 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 sta= ll + * 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 =3D 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 =3D usb_bus_from_device(dev); + int ret =3D 0; + + ret =3D usb_desc_handle_control(dev, p, request, value, index, length,= data); + if (ret >=3D 0) { + return; + } + + switch (request) { + case ClassInterfaceRequest | USBPRINTER_GET_DEVICE_ID: + ret =3D usb_printer_get_device_id(dev, request, value, index, + length, data); + if (ret < 0) { + goto error; + } + break; + + case ClassInterfaceRequest | USBPRINTER_GET_PORT_STATUS: + ret =3D 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 =3D 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 =3D ret; + p->status =3D USB_RET_SUCCESS; + return; + +error: + trace_usb_printer_handle_control_error(bus->busnr, dev->addr, request, + value, index, length); + p->status =3D USB_RET_STALL; +} + +static void usb_printer_handle_data_out(USBDevice *dev, USBPacket *p) +{ + USBBus *bus =3D usb_bus_from_device(dev); + QEMUIOVector *iov =3D p->combined ? &p->combined->iov : &p->iov; + + p->status =3D 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-rela= ted + * information." + */ +static void usb_printer_handle_data_in(USBDevice *dev, USBPacket *p) +{ + USBBus *bus =3D usb_bus_from_device(dev); + QEMUIOVector *iov =3D p->combined ? &p->combined->iov : &p->iov; + + p->status =3D 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 =3D 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 =3D USB_RET_STALL; + break; + } + + if (p->status =3D=3D 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 =3D USB_PRINTER(dev); + if (!s->terminal || strcmp(s->terminal, "printer")) { + error_setg(errp, "%s: support terminal printer only", TYPE_USB_PRI= NTER); + return; + } + + s->printer =3D 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 =3D &desc_printer; + + usb_desc_create_serial(dev); + usb_desc_init(dev); + s->dev.opaque =3D s; +} + +/* TODO: set alternates on IPP-over-USB */ +static void usb_printer_set_interface(USBDevice *dev, int iface, + int old, int value) +{ + USBBus *bus =3D usb_bus_from_device(dev); + trace_usb_printer_set_interface(bus->busnr, dev->addr, iface, old, val= ue); +} + +static Property usb_printer_properties[] =3D { + 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 =3D DEVICE_CLASS(klass); + USBDeviceClass *k =3D USB_DEVICE_CLASS(klass); + + device_class_set_props(dc, usb_printer_properties); + set_bit(DEVICE_CATEGORY_USB, dc->categories); + k->product_desc =3D "QEMU USB Printer Interface"; + k->realize =3D usb_printer_realize; + k->handle_reset =3D usb_printer_handle_reset; + k->handle_control =3D usb_printer_handle_control; + k->handle_data =3D usb_printer_handle_data; + k->unrealize =3D usb_printer_unrealize; + k->set_interface =3D usb_printer_set_interface; +} + +static const TypeInfo usb_printer_info =3D { + .name =3D TYPE_USB_PRINTER, + .parent =3D TYPE_USB_DEVICE, + .instance_size =3D sizeof(USBPrinterState), + .class_init =3D 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: f= iles('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')) =20 # 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) "de= v %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, val= ue 0x%x, index 0x%x, length 0x%x" +usb_printer_handle_data(int bus, int addr, int pid, int ep) "dev %d:%u dat= a, pid 0x%x, ep %d" +usb_printer_handle_data_out(int bus, int addr, int size) "dev %d:%u data o= ut, 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 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * 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 =3D 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 =3D 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 */ --=20 2.11.0