From nobody Wed Dec 17 19:02:37 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 54FE4153801 for ; Tue, 9 Jul 2024 08:45:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1720514754; cv=none; b=LgnzAHNs/9fHjXp86A4GcyG6VQGHJeA+hAYMAsRB1HEzeHxatRAknd011+cFylaw1kDozTT4QN+XWGGRErBUqAcon/QPE8R+mMzscOWfaBaK2Fl4XDxPN221nuoAmgM77vTMwzWjPkKh0ds8UWqpqxOCt1pYtouENNh6GeKiTDw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1720514754; c=relaxed/simple; bh=r1LXLS5QvZVpzBvAf+dbs9LewIWH4v56UjNIkLNAIPE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AV9oFZSbBrcAu+9I+F/i+safn2fWdQX600+jAp+vADDsBnuyBasW2ntJo6hORUCHEN6StUS4rSzD9G1GXS8R5EQM3G0ImSDk3EOm9eAPbjFbAC8BifQBka6+zRGudCkIwu4uQAlrN8L4tz2NOZV72NT63I54zUILwxke8uAGnG4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=cWb1mCVh; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="cWb1mCVh" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1720514750; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=MkuI1952GnwbwHYLrZQpgopRcUmmRrdu+MK6TPrEW2A=; b=cWb1mCVh5LphwF5rjVnFVcWnUfpiv1RI2juTmn15mhdUA7Eazv14RI9VqqlF7/TuG7DwQf gKHX5f4bOTWLhDWQAepx2QIUftXxgMVMOWDSfuqwUoO1vo40kS5joFy6JUC02Z55Zaia1e tkNNMi/339r0BZmI2D6u8vfAB0Gddoc= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-413-kRL4LkGgOEWqCkpPcTif_g-1; Tue, 09 Jul 2024 04:45:46 -0400 X-MC-Unique: kRL4LkGgOEWqCkpPcTif_g-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2F5B11954B09; Tue, 9 Jul 2024 08:45:44 +0000 (UTC) Received: from hydra.redhat.com (unknown [10.39.193.246]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1257219560AA; Tue, 9 Jul 2024 08:45:38 +0000 (UTC) From: Jocelyn Falempe To: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , "Bjorn Roy Baron" , Benno Lossin , Andreas Hindborg , Alice Ryhl , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Cc: Jocelyn Falempe Subject: [PATCH v2 4/4] drm/panic: Add a qr_code panic screen Date: Tue, 9 Jul 2024 10:40:10 +0200 Message-ID: <20240709084458.158659-5-jfalempe@redhat.com> In-Reply-To: <20240709084458.158659-1-jfalempe@redhat.com> References: <20240709084458.158659-1-jfalempe@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 Content-Type: text/plain; charset="utf-8" This patch adds a new panic screen, with a QR code and the kmsg data embedded. If DRM_PANIC_SCREEN_QR_CODE_URL is set, then the kmsg data will be compressed with zlib and encoded as a numerical segment, and appended to the url as a url parameter. This allows to save space, and put about ~7500 bytes of kmsg data, in a V40 QR code. Linux distributions can customize the url, and put a web frontend to directly open a bug report with the kmsg data. Otherwise the kmsg data will be encoded as binary segment (ie raw ascii) and only a maximum of 2953 bytes of kmsg data will be available in the QR code. You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION. v2: * Rewrite the rust comments with Markdown (Alice Ryhl) * Mark drm_panic_qr_generate() as unsafe (Alice Ryhl) * Use CStr directly, and remove the call to as_str_unchecked() (Alice Ryhl) * Add a check for data_len <=3D data_size (Greg KH) Signed-off-by: Jocelyn Falempe --- drivers/gpu/drm/Kconfig | 29 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_drv.c | 3 + drivers/gpu/drm/drm_panic.c | 247 ++++++++ drivers/gpu/drm/drm_panic_qr.rs | 1004 +++++++++++++++++++++++++++++++ include/drm/drm_panic.h | 4 + 6 files changed, 1288 insertions(+) create mode 100644 drivers/gpu/drm/drm_panic_qr.rs diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 6dd0016fc9cd..4029046d9952 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -149,6 +149,35 @@ config DRM_PANIC_SCREEN or by writing to /sys/module/drm/parameters/panic_screen sysfs entry Default is "user" =20 +config DRM_PANIC_SCREEN_QR_CODE + bool "Add a panic screen with a QR code" + depends on DRM_PANIC && RUST + help + This option adds a qr code generator, and a panic screen with a QR + code. The QR code will contain the last lines of kmsg and other debug + information. This should be easier for the user to report a kernel + panic, with all debug information available. + To use this panic screen, also set DRM_PANIC_SCREEN to "qr_code" + +config DRM_PANIC_SCREEN_QR_CODE_URL + string "Base url of the QR code in the panic screen" + depends on DRM_PANIC_SCREEN_QR_CODE + help + This option sets the base url to report the kernel panic. If it's set + the qr code will contain the url and the kmsg compressed with zlib as + url parameter. If it's empty, the qr code will contain the kmsg as + uncompressed text only. + +config DRM_PANIC_SCREEN_QR_VERSION + int "Maximum version (size) of the QR code." + depends on DRM_PANIC_SCREEN_QR_CODE + default 40 + help + This option limits the version (or size) of the QR code. QR code + version range from Version 1 (21x21) to Version 40 (177x177). + Smaller QR code are easier to read, but will contain less debugging + data. Default is 40. + config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" depends on STACKTRACE_SUPPORT diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 68cc9258ffc4..c62339b89d46 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -89,6 +89,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) +=3D \ drm_privacy_screen_x86.o drm-$(CONFIG_DRM_ACCEL) +=3D ../../accel/drm_accel.o drm-$(CONFIG_DRM_PANIC) +=3D drm_panic.o +drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) +=3D drm_panic_qr.o obj-$(CONFIG_DRM) +=3D drm.o =20 obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) +=3D drm_panel_orientation_quir= ks.o diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 93543071a500..27007b53a8c8 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -1067,6 +1067,7 @@ static const struct file_operations drm_stub_fops =3D= { static void drm_core_exit(void) { drm_privacy_screen_lookup_exit(); + drm_panic_exit(); accel_core_exit(); unregister_chrdev(DRM_MAJOR, "drm"); debugfs_remove(drm_debugfs_root); @@ -1099,6 +1100,8 @@ static int __init drm_core_init(void) if (ret < 0) goto error; =20 + drm_panic_init(); + drm_privacy_screen_lookup_init(); =20 drm_core_init_complete =3D true; diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index 450585374ca9..8c7895194a04 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include =20 #include #include @@ -26,6 +28,7 @@ #include #include #include +#include =20 MODULE_AUTHOR("Jocelyn Falempe"); MODULE_DESCRIPTION("DRM panic handler"); @@ -621,6 +624,232 @@ static void draw_panic_static_kmsg(struct drm_scanout= _buffer *sb) } } =20 +#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) +/* + * It is unwise to allocate memory in the panic callback, so the buffers a= re + * pre-allocated. Only 2 buffers and the zlib workspace are needed. + * Two buffers are enough, using the following buffer usage: + * 1) kmsg messages are dumped in buffer1 + * 2) kmsg is zlib-compressed into buffer2 + * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1 + * 4) QR-code image is generated in buffer2 + * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes = for + * data segments. + * + * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which f= its in + * a V40 QR-code (177x177). + * + * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will b= e put + * directly in the QR code. + * 1) kmsg messages are dumped in buffer1 + * 2) kmsg message is encoded as byte stream in buffer2 + * 3) QR-code image is generated in buffer1 + */ + +static uint panic_qr_version =3D CONFIG_DRM_PANIC_SCREEN_QR_VERSION; +module_param(panic_qr_version, uint, 0644); +MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code"= ); + +#define MAX_QR_DATA 2956 +#define MAX_ZLIB_RATIO 3 +#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4= 071 */ +#define QR_BUFFER2_SIZE 4096 +#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */ + +/* Compression parameters */ +#define COMPR_LEVEL 6 +#define WINDOW_BITS 12 +#define MEM_LEVEL 4 + +static char *qrbuf1; +static char *qrbuf2; +static struct z_stream_s stream; + +static void __init drm_panic_qr_init(void) +{ + qrbuf1 =3D kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL); + qrbuf2 =3D kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL); + stream.workspace =3D kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_= LEVEL), + GFP_KERNEL); +} + +static void drm_panic_qr_exit(void) +{ + kfree(qrbuf1); + qrbuf1 =3D NULL; + kfree(qrbuf2); + qrbuf2 =3D NULL; + kfree(stream.workspace); + stream.workspace =3D NULL; +} + +extern size_t drm_panic_qr_max_data_size(u8 version, size_t url_len); + +extern u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len= , size_t data_size, + u8 *tmp, size_t tmp_size); + +static int drm_panic_get_qr_code_url(u8 **qr_image) +{ + struct kmsg_dump_iter iter; + char url[256]; + size_t kmsg_len, max_kmsg_size; + char *kmsg; + int max_qr_data_size, url_len; + + url_len =3D snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_UR= L "?a=3D%s&v=3D%s&zl=3D", + utsname()->machine, utsname()->release); + + max_qr_data_size =3D drm_panic_qr_max_data_size(panic_qr_version, url_len= ); + max_kmsg_size =3D min(MAX_ZLIB_RATIO * max_qr_data_size, QR_BUFFER1_SIZE); + + /* get kmsg to buffer 1 */ + kmsg_dump_rewind(&iter); + kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len); + + if (!kmsg_len) + return -ENODATA; + kmsg =3D qrbuf1; + +try_again: + if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY) !=3D Z_OK) + return -EINVAL; + + stream.next_in =3D kmsg; + stream.avail_in =3D kmsg_len; + stream.total_in =3D 0; + stream.next_out =3D qrbuf2; + stream.avail_out =3D QR_BUFFER2_SIZE; + stream.total_out =3D 0; + + if (zlib_deflate(&stream, Z_FINISH) !=3D Z_STREAM_END) + return -EINVAL; + + if (zlib_deflateEnd(&stream) !=3D Z_OK) + return -EINVAL; + + if (stream.total_out > max_qr_data_size) { + /* too much data for the QR code, so skip the first line and try again */ + kmsg =3D strchr(kmsg + 1, '\n'); + if (!kmsg) + return -EINVAL; + kmsg_len =3D strlen(kmsg); + goto try_again; + } + *qr_image =3D qrbuf2; + + /* generate qr code image in buffer2 */ + return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SI= ZE, + qrbuf1, QR_BUFFER1_SIZE); +} + +static int drm_panic_get_qr_code_raw(u8 **qr_image) +{ + struct kmsg_dump_iter iter; + size_t kmsg_len; + size_t max_kmsg_size =3D min(drm_panic_qr_max_data_size(panic_qr_version,= 0), + QR_BUFFER1_SIZE); + + kmsg_dump_rewind(&iter); + kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len); + if (!kmsg_len) + return -ENODATA; + + *qr_image =3D qrbuf1; + return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, QR_BUFFER1_SIZE, + qrbuf2, QR_BUFFER2_SIZE); +} + +static int drm_panic_get_qr_code(u8 **qr_image) +{ + if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0) + return drm_panic_get_qr_code_url(qr_image); + else + return drm_panic_get_qr_code_raw(qr_image); +} + +/* + * Draw the panic message at the center of the screen, with a QR Code + */ +static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb) +{ + u32 fg_color =3D convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,= sb->format->format); + u32 bg_color =3D convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR,= sb->format->format); + const struct font_desc *font =3D get_default_font(sb->width, sb->height, = NULL, NULL); + struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas; + size_t max_qr_size, scale; + unsigned int panic_msg_width, qr_width, qr_canvas_width, v_margin; + u8 *qr_image; + unsigned int qr_pitch; + + if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace) + return -ENOMEM; + + r_screen =3D DRM_RECT_INIT(0, 0, sb->width, sb->height); + + drm_panic_logo_rect(&r_logo, font); + + panic_msg_width =3D get_max_line_len(panic_msg, panic_msg_lines) * font->= width; + r_msg =3D DRM_RECT_INIT(0, 0, + min(panic_msg_width, sb->width), + min(panic_msg_lines * font->height, sb->height)); + + max_qr_size =3D min(3 * sb->width / 4, 3 * sb->height / 4); + + qr_width =3D drm_panic_get_qr_code(&qr_image); + if (qr_width <=3D 0) + return -ENOSPC; + + qr_canvas_width =3D qr_width + QR_MARGIN * 2; + scale =3D max_qr_size / qr_canvas_width; + /* QR code is not readable if not scaled at least by 2 */ + if (scale < 2) + return -ENOSPC; + + pr_debug("QR width %d and scale %zu\n", qr_width, scale); + r_qr_canvas =3D DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_wi= dth * scale); + + v_margin =3D (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_heigh= t(&r_msg)) / 5; + + drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_= margin); + r_qr =3D DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1= + QR_MARGIN * scale, + qr_width * scale, qr_width * scale); + + /* Center the panic message */ + drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, + 3 * v_margin + drm_rect_height(&r_qr_canvas)); + + /* Fill with the background color, and draw text on top */ + drm_panic_fill(sb, &r_screen, bg_color); + + if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_q= r)) + drm_panic_logo_draw(sb, &r_logo, font, fg_color); + + draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg= _color); + + /* Draw the qr code */ + qr_pitch =3D DIV_ROUND_UP(qr_width, 8); + drm_panic_fill(sb, &r_qr_canvas, fg_color); + drm_panic_fill(sb, &r_qr, bg_color); + drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color); + return 0; +} + +static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) +{ + if (_draw_panic_static_qr_code(sb)) + draw_panic_static_user(sb); +} +#else +static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) +{ + draw_panic_static_user(sb); +} +static void drm_panic_qr_init(void) {}; +static void drm_panic_qr_exit(void) {}; +#endif + + /* * drm_panic_is_format_supported() * @format: a fourcc color code @@ -639,6 +868,8 @@ static void draw_panic_dispatch(struct drm_scanout_buff= er *sb) { if (!strcmp(drm_panic_screen, "kmsg")) { draw_panic_static_kmsg(sb); + } else if (!strcmp(drm_panic_screen, "qr_code")) { + draw_panic_static_qr_code(sb); } else { draw_panic_static_user(sb); } @@ -763,3 +994,19 @@ void drm_panic_unregister(struct drm_device *dev) } } EXPORT_SYMBOL(drm_panic_unregister); + +/** + * drm_panic_init() - initialize DRM panic. + */ +void __init drm_panic_init(void) +{ + drm_panic_qr_init(); +} + +/** + * drm_panic_exit() - Free the resources taken by drm_panic_exit() + */ +void drm_panic_exit(void) +{ + drm_panic_qr_exit(); +} diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_qr= .rs new file mode 100644 index 000000000000..75cd945bc01c --- /dev/null +++ b/drivers/gpu/drm/drm_panic_qr.rs @@ -0,0 +1,1004 @@ +// SPDX-License-Identifier: MIT + +//! This is a simple qr encoder for DRM panic. +//! +//! Due to the Panic constraint, it doesn't allocate memory and does all +//! the work on the stack or on the provided buffers. For +//! simplification, it only supports Low error correction, and apply the +//! first mask (checkboard). It will draw the smallest QRcode that can +//! contain the string passed as parameter. To get the most compact +//! QR-code, the start of the url is encoded as binary, and the +//! compressed kmsg is encoded as numeric. +//! +//! The binary data must be a valid url parameter, so the easiest way is +//! to use base64 encoding. But this waste 25% of data space, so the +//! whole stack trace won't fit in the QR-Code. So instead it encodes +//! every 13bits of input into 4 decimal digits, and then use the +//! efficient numeric encoding, that encode 3 decimal digits into +//! 10bits. This makes 39bits of compressed data into 12 decimal digits, +//! into 40bits in the QR-Code, so wasting only 2.5%. And numbers are +//! valid url parameter, so the website can do the reverse, to get the +//! binary data. +//! +//! Inspired by this 3 projects, all under MIT license: +//! +//! * +//! * +//! * + +use core::cmp; +use kernel::str::CStr; + +const __LOG_PREFIX: &[u8] =3D b"rust_qrcode\0"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] +struct Version(usize); + +// Generator polynomials for QR Code, only those that are needed for Low q= uality +const P7: [u8; 7] =3D [87, 229, 146, 149, 238, 102, 21]; +const P10: [u8; 10] =3D [251, 67, 46, 61, 118, 70, 64, 94, 32, 45]; +const P15: [u8; 15] =3D [ + 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105, +]; +const P18: [u8; 18] =3D [ + 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, = 98, 96, 153, +]; +const P20: [u8; 20] =3D [ + 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 16= 4, 212, 212, 188, 190, +]; +const P22: [u8; 22] =3D [ + 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80= , 219, 134, 160, 105, + 165, 231, +]; +const P24: [u8; 24] =3D [ + 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 2= 28, 218, 111, 0, 117, + 232, 87, 96, 227, 21, +]; +const P26: [u8; 26] =3D [ + 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, = 21, 245, 142, 13, 102, + 48, 227, 153, 145, 218, 70, +]; +const P28: [u8; 28] =3D [ + 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 2= 32, 201, 21, 43, 245, 87, + 42, 195, 212, 119, 242, 37, 9, 123, +]; +const P30: [u8; 30] =3D [ + 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 1= 25, 42, 173, 226, 193, + 224, 130, 156, 37, 251, 216, 238, 40, 192, 180, +]; + +/// QRCode parameter for Low quality ECC: +/// - Error Correction polynomial +/// - Number of blocks in group 1 +/// - Number of blocks in group 2 +/// - Block size in group 1 +/// (Block size in group 2 is one more than group 1) +struct VersionParameter(&'static [u8], u8, u8, u8); +const VPARAM: [VersionParameter; 40] =3D [ + VersionParameter(&P7, 1, 0, 19), // V1 + VersionParameter(&P10, 1, 0, 34), // V2 + VersionParameter(&P15, 1, 0, 55), // V3 + VersionParameter(&P20, 1, 0, 80), // V4 + VersionParameter(&P26, 1, 0, 108), // V5 + VersionParameter(&P18, 2, 0, 68), // V6 + VersionParameter(&P20, 2, 0, 78), // V7 + VersionParameter(&P24, 2, 0, 97), // V8 + VersionParameter(&P30, 2, 0, 116), // V9 + VersionParameter(&P18, 2, 2, 68), // V10 + VersionParameter(&P20, 4, 0, 81), // V11 + VersionParameter(&P24, 2, 2, 92), // V12 + VersionParameter(&P26, 4, 0, 107), // V13 + VersionParameter(&P30, 3, 1, 115), // V14 + VersionParameter(&P22, 5, 1, 87), // V15 + VersionParameter(&P24, 5, 1, 98), // V16 + VersionParameter(&P28, 1, 5, 107), // V17 + VersionParameter(&P30, 5, 1, 120), // V18 + VersionParameter(&P28, 3, 4, 113), // V19 + VersionParameter(&P28, 3, 5, 107), // V20 + VersionParameter(&P28, 4, 4, 116), // V21 + VersionParameter(&P28, 2, 7, 111), // V22 + VersionParameter(&P30, 4, 5, 121), // V23 + VersionParameter(&P30, 6, 4, 117), // V24 + VersionParameter(&P26, 8, 4, 106), // V25 + VersionParameter(&P28, 10, 2, 114), // V26 + VersionParameter(&P30, 8, 4, 122), // V27 + VersionParameter(&P30, 3, 10, 117), // V28 + VersionParameter(&P30, 7, 7, 116), // V29 + VersionParameter(&P30, 5, 10, 115), // V30 + VersionParameter(&P30, 13, 3, 115), // V31 + VersionParameter(&P30, 17, 0, 115), // V32 + VersionParameter(&P30, 17, 1, 115), // V33 + VersionParameter(&P30, 13, 6, 115), // V34 + VersionParameter(&P30, 12, 7, 121), // V35 + VersionParameter(&P30, 6, 14, 121), // V36 + VersionParameter(&P30, 17, 4, 122), // V37 + VersionParameter(&P30, 4, 18, 122), // V38 + VersionParameter(&P30, 20, 4, 117), // V39 + VersionParameter(&P30, 19, 6, 118), // V40 +]; + +const MAX_EC_SIZE: usize =3D 30; +const MAX_BLK_SIZE: usize =3D 123; + +/// Position of the alignment pattern grid +const ALIGNMENT_PATTERNS: [&[u8]; 40] =3D [ + &[], + &[6, 18], + &[6, 22], + &[6, 26], + &[6, 30], + &[6, 34], + &[6, 22, 38], + &[6, 24, 42], + &[6, 26, 46], + &[6, 28, 50], + &[6, 30, 54], + &[6, 32, 58], + &[6, 34, 62], + &[6, 26, 46, 66], + &[6, 26, 48, 70], + &[6, 26, 50, 74], + &[6, 30, 54, 78], + &[6, 30, 56, 82], + &[6, 30, 58, 86], + &[6, 34, 62, 90], + &[6, 28, 50, 72, 94], + &[6, 26, 50, 74, 98], + &[6, 30, 54, 78, 102], + &[6, 28, 54, 80, 106], + &[6, 32, 58, 84, 110], + &[6, 30, 58, 86, 114], + &[6, 34, 62, 90, 118], + &[6, 26, 50, 74, 98, 122], + &[6, 30, 54, 78, 102, 126], + &[6, 26, 52, 78, 104, 130], + &[6, 30, 56, 82, 108, 134], + &[6, 34, 60, 86, 112, 138], + &[6, 30, 58, 86, 114, 142], + &[6, 34, 62, 90, 118, 146], + &[6, 30, 54, 78, 102, 126, 150], + &[6, 24, 50, 76, 102, 128, 154], + &[6, 28, 54, 80, 106, 132, 158], + &[6, 32, 58, 84, 110, 136, 162], + &[6, 26, 54, 82, 110, 138, 166], + &[6, 30, 58, 86, 114, 142, 170], +]; + +/// Version information for format V7-V40 +const VERSION_INFORMATION: [u32; 34] =3D [ + 0b00_0111_1100_1001_0100, + 0b00_1000_0101_1011_1100, + 0b00_1001_1010_1001_1001, + 0b00_1010_0100_1101_0011, + 0b00_1011_1011_1111_0110, + 0b00_1100_0111_0110_0010, + 0b00_1101_1000_0100_0111, + 0b00_1110_0110_0000_1101, + 0b00_1111_1001_0010_1000, + 0b01_0000_1011_0111_1000, + 0b01_0001_0100_0101_1101, + 0b01_0010_1010_0001_0111, + 0b01_0011_0101_0011_0010, + 0b01_0100_1001_1010_0110, + 0b01_0101_0110_1000_0011, + 0b01_0110_1000_1100_1001, + 0b01_0111_0111_1110_1100, + 0b01_1000_1110_1100_0100, + 0b01_1001_0001_1110_0001, + 0b01_1010_1111_1010_1011, + 0b01_1011_0000_1000_1110, + 0b01_1100_1100_0001_1010, + 0b01_1101_0011_0011_1111, + 0b01_1110_1101_0111_0101, + 0b01_1111_0010_0101_0000, + 0b10_0000_1001_1101_0101, + 0b10_0001_0110_1111_0000, + 0b10_0010_1000_1011_1010, + 0b10_0011_0111_1001_1111, + 0b10_0100_1011_0000_1011, + 0b10_0101_0100_0010_1110, + 0b10_0110_1010_0110_0100, + 0b10_0111_0101_0100_0001, + 0b10_1000_1100_0110_1001, +]; + +/// Format info for Low EC +const FORMAT_INFOS_QR_L: [u16; 8] =3D [ + 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, +]; + +impl Version { + // Return the smallest QR Version than can hold these segments + fn from_segments(segments: &[&Segment<'_>]) -> Option { + for v in (1..=3D40).map(|k| Version(k)) { + if v.max_data() * 8 >=3D segments.iter().map(|s| s.total_size_= bits(v)).sum() { + return Some(v); + } + } + None + } + + fn width(&self) -> u8 { + (self.0 as u8) * 4 + 17 + } + + fn max_data(&self) -> usize { + self.g1_blk_size() * self.g1_blocks() + (self.g1_blk_size() + 1) *= self.g2_blocks() + } + + fn ec_size(&self) -> usize { + VPARAM[self.0 - 1].0.len() + } + + fn g1_blocks(&self) -> usize { + VPARAM[self.0 - 1].1 as usize + } + + fn g2_blocks(&self) -> usize { + VPARAM[self.0 - 1].2 as usize + } + + fn g1_blk_size(&self) -> usize { + VPARAM[self.0 - 1].3 as usize + } + + fn alignment_pattern(&self) -> &'static [u8] { + &ALIGNMENT_PATTERNS[self.0 - 1] + } + + fn poly(&self) -> &'static [u8] { + VPARAM[self.0 - 1].0 + } + + fn version_info(&self) -> u32 { + if *self >=3D Version(7) { + VERSION_INFORMATION[self.0 - 7] + } else { + 0 + } + } +} + +/// Exponential table for Galois Field GF(256) +const EXP_TABLE: [u8; 256] =3D [ + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 1= 52, 45, 90, 180, 117, + 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 14= 8, 53, 106, 212, 181, + 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105,= 210, 185, 111, 222, 161, + 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 24= 0, 253, 231, 211, 187, + 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 6= 7, 134, 17, 34, 68, 136, + 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, = 147, 59, 118, 236, 197, + 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 6= 6, 132, 21, 42, 84, 168, + 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 2= 30, 209, 191, 99, 198, + 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 7= 5, 150, 49, 98, 196, 149, + 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28,= 56, 112, 224, 221, 167, + 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, = 69, 138, 9, 18, 36, 72, + 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, = 176, 125, 250, 233, 207, + 131, 27, 54, 108, 216, 173, 71, 142, 1, +]; + +/// Reverse exponential table for Galois Field GF(256) +const LOG_TABLE: [u8; 256] =3D [ + 175, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 1= 00, 224, 14, 52, 141, + 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36= , 15, 33, 53, 147, 142, + 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154,= 9, 120, 77, 228, 114, + 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 3= 4, 136, 54, 208, 148, + 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 18= 2, 163, 195, 72, 126, + 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 12= 1, 43, 78, 212, 229, 172, + 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 2= 37, 49, 197, 254, 24, + 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, = 46, 55, 63, 209, 91, 149, + 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 17= 1, 20, 42, 93, 158, 132, + 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196= , 23, 73, 236, 127, 12, + 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, = 204, 62, 90, 203, 89, 95, + 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, = 213, 233, 230, 231, 173, + 232, 116, 214, 244, 234, 168, 80, 88, 175, +]; + +// 4 bits segment header +const MODE_STOP: u16 =3D 0; +const MODE_NUMERIC: u16 =3D 1; +const MODE_BINARY: u16 =3D 4; +// padding bytes +const PADDING: [u8; 2] =3D [236, 17]; + +/// get the next 13 bits of data, starting at specified offset (in bits) +fn get_next_13b(data: &[u8], offset: usize) -> Option<(u16, usize)> { + if offset < data.len() * 8 { + let size =3D cmp::min(13, data.len() * 8 - offset); + let byte_off =3D offset / 8; + let bit_off =3D offset % 8; + // b is 20 at max (bit_off <=3D 7 and size <=3D 13) + let b =3D (bit_off + size) as u16; + + let first_byte =3D (data[byte_off] << bit_off >> bit_off) as u16; + + let number =3D match b { + 0..=3D8 =3D> first_byte >> (8 - b), + 9..=3D16 =3D> (first_byte << (b - 8)) + (data[byte_off + 1] >>= (16 - b)) as u16, + _ =3D> { + (first_byte << (b - 8)) + + ((data[byte_off + 1] as u16) << (b - 16)) + + (data[byte_off + 2] >> (24 - b)) as u16 + } + }; + Some((number, size)) + } else { + None + } +} + +/// number of bits to encode characters in numeric mode. +const NUM_CHARS_BITS: [usize; 4] =3D [0, 4, 7, 10]; +const POW10: [u16; 4] =3D [1, 10, 100, 1000]; + +enum Segment<'a> { + Numeric(&'a [u8]), + Binary(&'a [u8]), +} + +impl Segment<'_> { + fn get_header(&self) -> (u16, usize) { + match self { + Segment::Binary(_) =3D> (MODE_BINARY, 4), + Segment::Numeric(_) =3D> (MODE_NUMERIC, 4), + } + } + + // Return the size of the length field in bits, depending on QR Versio= n. + fn length_bits_count(&self, version: Version) -> usize { + let Version(v) =3D version; + match self { + Segment::Binary(_) =3D> match v { + 1..=3D9 =3D> 8, + _ =3D> 16, + }, + Segment::Numeric(_) =3D> match v { + 1..=3D9 =3D> 10, + 10..=3D26 =3D> 12, + _ =3D> 14, + }, + } + } + + // Number of characters in the segment + fn character_count(&self) -> usize { + match self { + Segment::Binary(data) =3D> data.len(), + Segment::Numeric(data) =3D> { + let data_bits =3D data.len() * 8; + let last_chars =3D match data_bits % 13 { + 1 =3D> 1, + k =3D> (k + 1) / 3, + }; + // 4 decimal numbers per 13bits + remainder + 4 * (data_bits / 13) + last_chars + } + } + } + + fn get_length_field(&self, version: Version) -> (u16, usize) { + ( + self.character_count() as u16, + self.length_bits_count(version), + ) + } + + fn total_size_bits(&self, version: Version) -> usize { + let data_size =3D match self { + Segment::Binary(data) =3D> data.len() * 8, + Segment::Numeric(_) =3D> { + let digits =3D self.character_count(); + 10 * (digits / 3) + NUM_CHARS_BITS[digits % 3] + } + }; + // header + length + data + 4 + self.length_bits_count(version) + data_size + } + + fn iter(&self) -> SegmentIterator<'_> { + SegmentIterator { + segment: self, + offset: 0, + carry: 0, + carry_len: 0, + } + } +} + +struct SegmentIterator<'a> { + segment: &'a Segment<'a>, + offset: usize, + carry: u16, + carry_len: usize, +} + +impl Iterator for SegmentIterator<'_> { + type Item =3D (u16, usize); + + fn next(&mut self) -> Option { + match self.segment { + Segment::Binary(data) =3D> { + if self.offset < data.len() { + let byte =3D data[self.offset] as u16; + self.offset +=3D 1; + Some((byte, 8)) + } else { + None + } + } + Segment::Numeric(data) =3D> { + if self.carry_len =3D=3D 3 { + let out =3D (self.carry, NUM_CHARS_BITS[self.carry_len= ]); + self.carry_len =3D 0; + self.carry =3D 0; + Some(out) + } else if let Some((bits, size)) =3D get_next_13b(data, se= lf.offset) { + self.offset +=3D size; + let new_chars =3D match size { + 1 =3D> 1, + k =3D> (k + 1) / 3, + }; + if self.carry_len + new_chars > 3 { + self.carry_len =3D new_chars + self.carry_len - 3; + let out =3D ( + self.carry * POW10[new_chars - self.carry_len] + + bits / POW10[self.carry_len], + NUM_CHARS_BITS[3], + ); + self.carry =3D bits % POW10[self.carry_len]; + Some(out) + } else { + let out =3D ( + self.carry * POW10[new_chars] + bits, + NUM_CHARS_BITS[self.carry_len + new_chars], + ); + self.carry_len =3D 0; + Some(out) + } + } else if self.carry_len > 0 { + let out =3D (self.carry, NUM_CHARS_BITS[self.carry_len= ]); + self.carry_len =3D 0; + Some(out) + } else { + None + } + } + } + } +} + +struct EncodedMsg<'a> { + data: &'a mut [u8], + ec_size: usize, + g1_blocks: usize, + g2_blocks: usize, + g1_blk_size: usize, + g2_blk_size: usize, + poly: &'static [u8], + version: Version, +} + +/// EncodedMsg will hold the data to be put in the QR-Code, with correct s= egment +/// encoding, padding, and Error Code Correction. +/// It also implements an iterator to retrieve the data interleaved to dra= w the +/// QR-code image. +impl EncodedMsg<'_> { + fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> Optio= n> { + let version =3D Version::from_segments(segments)?; + let ec_size =3D version.ec_size(); + let g1_blocks =3D version.g1_blocks(); + let g2_blocks =3D version.g2_blocks(); + let g1_blk_size =3D version.g1_blk_size(); + let g2_blk_size =3D g1_blk_size + 1; + let poly =3D version.poly(); + + // clear the output + data.fill(0); + + let mut em =3D EncodedMsg { + data: data, + ec_size, + g1_blocks, + g2_blocks, + g1_blk_size, + g2_blk_size, + poly, + version, + }; + em.encode(segments); + Some(em) + } + + /// push bits of data at an offset (in bits) + fn push(&mut self, offset: &mut usize, bits: (u16, usize)) { + let (number, len_bits) =3D bits; + let byte_off =3D *offset / 8; + let bit_off =3D *offset % 8; + let b =3D bit_off + len_bits; + + match (bit_off, b) { + (0, 0..=3D8) =3D> { + self.data[byte_off] =3D (number << (8 - b)) as u8; + } + (0, _) =3D> { + self.data[byte_off] =3D (number >> (b - 8)) as u8; + self.data[byte_off + 1] =3D (number << (16 - b)) as u8; + } + (_, 0..=3D8) =3D> { + self.data[byte_off] |=3D (number << (8 - b)) as u8; + } + (_, 9..=3D16) =3D> { + self.data[byte_off] |=3D (number >> (b - 8)) as u8; + self.data[byte_off + 1] =3D (number << (16 - b)) as u8; + } + _ =3D> { + self.data[byte_off] |=3D (number >> (b - 8)) as u8; + self.data[byte_off + 1] =3D (number >> (b - 16)) as u8; + self.data[byte_off + 2] =3D (number << (24 - b)) as u8; + } + } + *offset +=3D len_bits; + } + + fn add_segments(&mut self, segments: &[&Segment<'_>]) { + let mut offset: usize =3D 0; + + for s in segments.iter() { + self.push(&mut offset, s.get_header()); + self.push(&mut offset, s.get_length_field(self.version)); + for bits in s.iter() { + self.push(&mut offset, bits); + } + } + self.push(&mut offset, (MODE_STOP, 4)); + + let pad_offset =3D (offset + 7) / 8; + for i in pad_offset..self.version.max_data() { + self.data[i] =3D PADDING[(i & 1) ^ (pad_offset & 1)]; + } + } + + fn error_code_for_blocks(&mut self, offset: usize, size: usize, ec_off= set: usize) { + let mut tmp: [u8; MAX_BLK_SIZE + MAX_EC_SIZE] =3D [0; MAX_BLK_SIZE= + MAX_EC_SIZE]; + + tmp[0..size].copy_from_slice(&self.data[offset..offset + size]); + for i in 0..size { + let lead_coeff =3D tmp[i] as usize; + if lead_coeff =3D=3D 0 { + continue; + } + let log_lead_coeff =3D usize::from(LOG_TABLE[lead_coeff]); + for (u, &v) in tmp[i + 1..].iter_mut().zip(self.poly.iter()) { + *u ^=3D EXP_TABLE[(usize::from(v) + log_lead_coeff) % 255]; + } + } + self.data[ec_offset..ec_offset + self.ec_size] + .copy_from_slice(&tmp[size..size + self.ec_size]); + } + + fn compute_error_code(&mut self) { + let mut offset =3D 0; + let mut ec_offset =3D self.g1_blocks * self.g1_blk_size + self.g2_= blocks * self.g2_blk_size; + + for _ in 0..self.g1_blocks { + self.error_code_for_blocks(offset, self.g1_blk_size, ec_offset= ); + offset +=3D self.g1_blk_size; + ec_offset +=3D self.ec_size; + } + for _ in 0..self.g2_blocks { + self.error_code_for_blocks(offset, self.g2_blk_size, ec_offset= ); + offset +=3D self.g2_blk_size; + ec_offset +=3D self.ec_size; + } + } + + fn encode(&mut self, segments: &[&Segment<'_>]) { + self.add_segments(segments); + self.compute_error_code(); + } + + fn iter(&self) -> EncodedMsgIterator<'_> { + EncodedMsgIterator { + em: self, + offset: 0, + } + } +} + +struct EncodedMsgIterator<'a> { + em: &'a EncodedMsg<'a>, + offset: usize, +} + +impl Iterator for EncodedMsgIterator<'_> { + type Item =3D u8; + + // send the bytes in interleaved mode, first byte of first block of gr= oup1, then first byte of + // second block of group1, ... + fn next(&mut self) -> Option { + let em =3D self.em; + let blocks =3D em.g1_blocks + em.g2_blocks; + let g1_end =3D em.g1_blocks * em.g1_blk_size; + let g2_end =3D g1_end + em.g2_blocks * em.g2_blk_size; + let ec_end =3D g2_end + em.ec_size * blocks; + + if self.offset >=3D ec_end { + return None; + } + + let offset =3D if self.offset < em.g1_blk_size * blocks { + // group1 and group2 interleaved + let blk =3D self.offset % blocks; + let blk_off =3D self.offset / blocks; + if blk < em.g1_blocks { + blk * em.g1_blk_size + blk_off + } else { + g1_end + em.g2_blk_size * (blk - em.g1_blocks) + blk_off + } + } else if self.offset < g2_end { + // last byte of group2 blocks + let blk2 =3D self.offset - blocks * em.g1_blk_size; + em.g1_blk_size * em.g1_blocks + blk2 * em.g2_blk_size + em.g2_= blk_size - 1 + } else { + // EC blocks + let ec_offset =3D self.offset - g2_end; + let blk =3D ec_offset % blocks; + let blk_off =3D ec_offset / blocks; + + g2_end + blk * em.ec_size + blk_off + }; + self.offset +=3D 1; + Some(em.data[offset]) + } +} + +/// QrImage +/// +/// A QR-Code image, encoded as a linear binary framebuffer. +/// Max width is 177 for V40 QR code, so u8 is enough for coordinate. +struct QrImage<'a> { + data: &'a mut [u8], + width: u8, + stride: u8, + version: Version, +} + +impl QrImage<'_> { + fn new<'a, 'b>(em: &'b EncodedMsg<'b>, qrdata: &'a mut [u8]) -> QrImag= e<'a> { + let width =3D em.version.width(); + let stride =3D (width + 7) / 8; + let data =3D qrdata; + + let mut qr_image =3D QrImage { + data, + width, + stride, + version: em.version, + }; + qr_image.draw_all(em.iter()); + qr_image + } + + fn clear(&mut self) { + self.data.fill(0); + } + + // set pixel to light color + fn set(&mut self, x: u8, y: u8) { + let off =3D y as usize * self.stride as usize + x as usize / 8; + let mut v =3D self.data[off]; + v |=3D 0x80 >> (x % 8); + self.data[off] =3D v; + } + + // Invert a pixel color + fn xor(&mut self, x: u8, y: u8) { + let off =3D y as usize * self.stride as usize + x as usize / 8; + self.data[off] ^=3D 0x80 >> (x % 8); + } + + // Draw a light square at (x, y) top left corner + fn draw_square(&mut self, x: u8, y: u8, size: u8) { + for k in 0..size { + self.set(x + k, y); + self.set(x, y + k + 1); + self.set(x + size, y + k); + self.set(x + k + 1, y + size); + } + } + + // Finder pattern, 3 8x8 square at the corners + fn draw_finders(&mut self) { + self.draw_square(1, 1, 4); + self.draw_square(self.width - 6, 1, 4); + self.draw_square(1, self.width - 6, 4); + for k in 0..8 { + self.set(k, 7); + self.set(self.width - k - 1, 7); + self.set(k, self.width - 8); + } + for k in 0..7 { + self.set(7, k); + self.set(self.width - 8, k); + self.set(7, self.width - 1 - k); + } + } + + fn is_finder(&self, x: u8, y: u8) -> bool { + let end =3D self.width - 8; + (x < 8 && y < 8) || (x < 8 && y >=3D end) || (x >=3D end && y < 8) + } + + // Alignment pattern, 5x5 squares in a grid + fn draw_alignments(&mut self) { + let positions =3D self.version.alignment_pattern(); + for &x in positions.iter() { + for &y in positions.iter() { + if !self.is_finder(x, y) { + self.draw_square(x - 1, y - 1, 2); + } + } + } + } + + fn is_alignment(&self, x: u8, y: u8) -> bool { + let positions =3D self.version.alignment_pattern(); + for &ax in positions.iter() { + for &ay in positions.iter() { + if self.is_finder(ax, ay) { + continue; + } + if x >=3D ax - 2 && x <=3D ax + 2 && y >=3D ay - 2 && y <= =3D ay + 2 { + return true; + } + } + } + false + } + + // Timing pattern, 2 dotted line between the finder patterns + fn draw_timing_patterns(&mut self) { + let end =3D self.width - 8; + + for x in (9..end).step_by(2) { + self.set(x, 6); + self.set(6, x); + } + } + + fn is_timing(&self, x: u8, y: u8) -> bool { + x =3D=3D 6 || y =3D=3D 6 + } + + // mask info : 15 bits around the finders, written twice for redundancy + fn draw_maskinfo(&mut self) { + let info: u16 =3D FORMAT_INFOS_QR_L[0]; + let mut skip =3D 0; + + for k in 0..7 { + if k =3D=3D 6 { + skip =3D 1; + } + if info & (1 << (14 - k)) =3D=3D 0 { + self.set(k + skip, 8); + self.set(8, self.width - 1 - k); + } + } + skip =3D 0; + for k in 0..8 { + if k =3D=3D 2 { + skip =3D 1; + } + if info & (1 << (7 - k)) =3D=3D 0 { + self.set(8, 8 - skip - k); + self.set(self.width - 8 + k, 8); + } + } + } + + fn is_maskinfo(&self, x: u8, y: u8) -> bool { + let end =3D self.width - 8; + // Count the dark module as mask info + (x <=3D 8 && y =3D=3D 8) || (y <=3D 8 && x =3D=3D 8) || (x =3D=3D = 8 && y >=3D end) || (x >=3D end && y =3D=3D 8) + } + + // Version info are 18bits written twice, close to the finders + fn draw_version_info(&mut self) { + let vinfo =3D self.version.version_info(); + let pos =3D self.width - 11; + + if vinfo !=3D 0 { + for x in 0..3 { + for y in 0..6 { + if vinfo & (1 << (x + y * 3)) =3D=3D 0 { + self.set(x + pos, y); + self.set(y, x + pos); + } + } + } + } + } + + fn is_version_info(&self, x: u8, y: u8) -> bool { + let vinfo =3D self.version.version_info(); + let pos =3D self.width - 11; + + vinfo !=3D 0 && ((x >=3D pos && x < pos + 3 && y < 6) || (y >=3D p= os && y < pos + 3 && x < 6)) + } + + // Return true if the pixel is reserved (ie not usable for data and EC) + fn is_reserved(&self, x: u8, y: u8) -> bool { + self.is_alignment(x, y) + || self.is_finder(x, y) + || self.is_timing(x, y) + || self.is_maskinfo(x, y) + || self.is_version_info(x, y) + } + + fn is_last(&self, x: u8, y: u8) -> bool { + x =3D=3D 0 && y =3D=3D self.width - 1 + } + + // Move to the next pixel according to QR code order + // from bottom right corner, to bottom left corner + fn next(&self, x: u8, y: u8) -> (u8, u8) { + let x_adj =3D if x <=3D 6 { x + 1 } else { x }; + let column_type =3D (self.width - x_adj) % 4; + + match column_type { + 2 if y > 0 =3D> (x + 1, y - 1), + 0 if y < self.width - 1 =3D> (x + 1, y + 1), + 0 | 2 if x =3D=3D 7 =3D> (x - 2, y), + _ =3D> (x - 1, y), + } + } + + // Find next pixel that can hold data + fn next_available(&self, x: u8, y: u8) -> (u8, u8) { + let (mut x, mut y) =3D self.next(x, y); + while self.is_reserved(x, y) && !self.is_last(x, y) { + (x, y) =3D self.next(x, y); + } + (x, y) + } + + fn draw_data(&mut self, data: impl Iterator) { + let (mut x, mut y) =3D (self.width - 1, self.width - 1); + for byte in data { + for s in 0..8 { + if byte & (0x80 >> s) =3D=3D 0 { + self.set(x, y); + } + (x, y) =3D self.next_available(x, y); + } + } + // set the remaining pixels (3 or 7 depending on version) + // because 0 correspond to a light module. + while !self.is_last(x, y) { + if !self.is_reserved(x, y) { + self.set(x, y); + } + (x, y) =3D self.next(x, y); + } + } + + // Apply checkboard mask to all non-reserved modules + fn apply_mask(&mut self) { + for x in 0..self.width { + for y in 0..self.width { + if (x ^ y) % 2 =3D=3D 0 && !self.is_reserved(x, y) { + self.xor(x, y); + } + } + } + } + + // draw the qrcode with the provided data iterator + fn draw_all(&mut self, data: impl Iterator) { + // first clear the table, as it may have already some data. + self.clear(); + self.draw_finders(); + self.draw_alignments(); + self.draw_timing_patterns(); + self.draw_version_info(); + self.draw_data(data); + self.draw_maskinfo(); + self.apply_mask(); + } +} + +/// drm_panic_qr_generate() +/// +/// C entry point for the rust QR Code generator. +/// +/// Write the QR code image in the data buffer, and return the qrcode size= , or 0 +/// if the data doesn't fit in a QR code. +/// +/// * `url` The base url of the QR code. It will be encoded as Binary segm= ent. +/// * `data` A pointer to the binary data, to be encoded. if url is NULL, = it +/// will be encoded as binary segment, otherwise it will be encoded +/// efficiently as a numeric segment, and appended to the url. +/// * `data_len` Length of the data, that needs to be encoded. +/// * `data_size` Size of data buffer, it should be at least 4071 bytes to= hold +/// a V40 QR-code. It will then be overwritten with the QR-code image. +/// * `tmp` A temporary buffer that the QR-code encoder will use, to write= the +/// segments and ECC. +/// * `tmp_size` Size of the temporary buffer, it must be at least 3706 by= tes +/// long for V40. +/// +/// # Safety +/// +/// * `url` must be null or point at a nul-terminated string. +/// * `data` must be valid for reading and writing for `data_size` bytes. +/// * `data_len` must be less than `data_size`. +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes. + +#[no_mangle] +pub unsafe extern "C" fn drm_panic_qr_generate( + url: *const i8, + data: *mut u8, + data_len: usize, + data_size: usize, + tmp: *mut u8, + tmp_size: usize, +) -> u8 { + if data_size <=3D 4071 || tmp_size <=3D 3706 || data_len > data_size { + return 0; + } + // Safety: data must be a valid pointer for reading and writing data_s= ize bytes. + let data_slice: &mut [u8] =3D unsafe { core::slice::from_raw_parts_mut= (data, data_size) }; + // Safety: tmp must be a valid pointer for reading and writing tmp_siz= e bytes. + let tmp_slice: &mut [u8] =3D unsafe { core::slice::from_raw_parts_mut(= tmp, tmp_size) }; + if url.is_null() { + match EncodedMsg::new(&[&Segment::Binary(&data_slice[0..data_len])= ], tmp_slice) { + None =3D> 0, + Some(em) =3D> { + let qr_image =3D QrImage::new(&em, data_slice); + qr_image.width + } + } + } else { + // Safety: url must be a valid pointer to a nul-terminated string. + let url_cstr: &CStr =3D unsafe { CStr::from_char_ptr(url) }; + let segments =3D &[ + &Segment::Binary(url_cstr.as_bytes()), + &Segment::Numeric(&data_slice[0..data_len]), + ]; + match EncodedMsg::new(segments, tmp_slice) { + None =3D> 0, + Some(em) =3D> { + let qr_image =3D QrImage::new(&em, data_slice); + qr_image.width + } + } + } +} + +/// drm_panic_qr_max_data_size() +/// +/// * `version` QR code version, between 1-40. +/// * `url_len` Length of the url. +/// +/// Returns the maximum data size that can fit in a QR code of this versio= n. +/// * If url_len > 0, remove the 2 segments header/length and also count t= he +/// conversion to numeric segments. +/// * If url_len =3D 0, only removes 3 bytes for 1 binary segment. +#[no_mangle] +pub extern "C" fn drm_panic_qr_max_data_size(version: u8, url_len: usize) = -> usize { + if version < 1 || version > 40 { + return 0; + } + let max_data =3D Version(version as usize).max_data(); + + if url_len > 0 { + // binary segment (url) 4 + 16 bits, numeric segment(kmsg) 4 + 12 = bits =3D> 5 bytes + if url_len + 5 >=3D max_data { + 0 + } else { + let max =3D max_data - url_len - 5; + (max * 39) / 40 + } + } else { + // remove 3 bytes for the binary segment (header 4 bits , length 1= 6 bits, stop 4bits) + max_data - 3 + } +} diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h index 73bb3f3d9ed9..7631548e7bbb 100644 --- a/include/drm/drm_panic.h +++ b/include/drm/drm_panic.h @@ -150,11 +150,15 @@ struct drm_scanout_buffer { =20 void drm_panic_register(struct drm_device *dev); void drm_panic_unregister(struct drm_device *dev); +void drm_panic_init(void); +void drm_panic_exit(void); =20 #else =20 static inline void drm_panic_register(struct drm_device *dev) {} static inline void drm_panic_unregister(struct drm_device *dev) {} +static inline void drm_panic_init(void) {} +static inline void drm_panic_exit(void) {} =20 #endif =20 --=20 2.45.2