lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <d63037ae-7580-a566-17e8-08f0b888ff4a@gmail.com>
Date: Mon, 22 Jul 2024 16:20:50 -0700
From: Mitchell Levy <levymitchell0@...il.com>
To: Jocelyn Falempe <jfalempe@...hat.com>,
 Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>,
 Maxime Ripard <mripard@...nel.org>, Thomas Zimmermann <tzimmermann@...e.de>,
 David Airlie <airlied@...il.com>, Daniel Vetter <daniel@...ll.ch>,
 Miguel Ojeda <ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>,
 Wedson Almeida Filho <wedsonaf@...il.com>, Boqun Feng
 <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
 Bjorn Roy Baron <bjorn3_gh@...tonmail.com>,
 Benno Lossin <benno.lossin@...ton.me>,
 Andreas Hindborg <a.hindborg@...sung.com>, Alice Ryhl
 <aliceryhl@...gle.com>, linux-kernel@...r.kernel.org,
 dri-devel@...ts.freedesktop.org, rust-for-linux@...r.kernel.org,
 Danilo Krummrich <dakr@...hat.com>
Subject: Re: [PATCH v4 4/4] drm/panic: Add a QR code panic screen

On 7/17/2024 7:24 AM, Jocelyn Falempe wrote:
> 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 a binary segment (ie raw
> ascii) and only a maximum of 2953 bytes of kmsg data will be
> available in the QR code.

It seems like this can cause some QR code readers to misbehave. For 
example, if the QR code includes an 0xfe character (eg by `echo -e 
'\xfe' > /dev/kmsg`) the Android QR code scanner on my phone refuses to 
scan it. Very arguably this is a bug in the scanner (interestingly, the 
QR code scanner built into Google's camera app handles this case fine), 
but I'm not sure what the QR code spec has to say on this matter.

It seems like dmesg handles this case by escaping the invalid byte into 
the four byte string "\xfe". Of course, this adds some complexity 
because the bytes encoded in the output and the bytes in the kmsg data 
are no longer 1-to-1.

> You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION.
> 
> Signed-off-by: Jocelyn Falempe <jfalempe@...hat.com>
> ---
> 
> 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 <= data_size (Greg KH)
> 
> v3:
>   * Fix all rust comments (typo, punctuation) (Miguel Ojeda)
>   * Change the wording of safety comments (Alice Ryhl)
>   * Add a link to the javascript decoder in the Kconfig (Greg KH)
>   * Fix data_size and tmp_size check in drm_panic_qr_generate()
> 
> v4:
>   * Fix the logic to find next line and skip the '\n' (Alic Ryhl)
>   * Remove __LOG_PREFIX as it's not used (Alice Ryhl)
> 
> 
>   drivers/gpu/drm/Kconfig         |   31 +
>   drivers/gpu/drm/Makefile        |    1 +
>   drivers/gpu/drm/drm_drv.c       |    3 +
>   drivers/gpu/drm/drm_panic.c     |  249 ++++++++
>   drivers/gpu/drm/drm_panic_qr.rs | 1003 +++++++++++++++++++++++++++++++
>   include/drm/drm_panic.h         |    4 +
>   6 files changed, 1291 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..50ac967b56be 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -149,6 +149,37 @@ config DRM_PANIC_SCREEN
>   	  or by writing to /sys/module/drm/parameters/panic_screen sysfs entry
>   	  Default is "user"
>   
> +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
> +	  a URL parameter. If it's empty, the QR code will contain the kmsg as
> +	  uncompressed text only.
> +	  There is a demo code in javascript, to decode and uncompress the kmsg
> +	  data from the URL parameter at https://github.com/kdj0c/panic_report
> +
> +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 ranges 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) += \
>   	drm_privacy_screen_x86.o
>   drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o
>   drm-$(CONFIG_DRM_PANIC) += drm_panic.o
> +drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o
>   obj-$(CONFIG_DRM)	+= drm.o
>   
>   obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.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 = {
>   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;
>   
> +	drm_panic_init();
> +
>   	drm_privacy_screen_lookup_init();
>   
>   	drm_core_init_complete = true;
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> index 1fbefb99cf6e..1357d910b742 100644
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
> @@ -18,6 +18,8 @@
>   #include <linux/overflow.h>
>   #include <linux/printk.h>
>   #include <linux/types.h>
> +#include <linux/utsname.h>
> +#include <linux/zlib.h>
>   
>   #include <drm/drm_drv.h>
>   #include <drm/drm_fourcc.h>
> @@ -26,6 +28,7 @@
>   #include <drm/drm_panic.h>
>   #include <drm/drm_plane.h>
>   #include <drm/drm_print.h>
> +#include <drm/drm_rect.h>
>   
>   MODULE_AUTHOR("Jocelyn Falempe");
>   MODULE_DESCRIPTION("DRM panic handler");
> @@ -621,6 +624,234 @@ static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
>   	}
>   }
>   
> +#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
> +/*
> + * It is unwise to allocate memory in the panic callback, so the buffers are
> + * 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 fits in
> + * a V40 QR-code (177x177).
> + *
> + * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be 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 = 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 > 4071  */
> +#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 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL);
> +	qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL);
> +	stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
> +				   GFP_KERNEL);
> +}
> +
> +static void drm_panic_qr_exit(void)
> +{
> +	kfree(qrbuf1);
> +	qrbuf1 = NULL;
> +	kfree(qrbuf2);
> +	qrbuf2 = NULL;
> +	kfree(stream.workspace);
> +	stream.workspace = 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 = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&zl=",
> +			   utsname()->machine, utsname()->release);
> +
> +	max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
> +	max_kmsg_size = 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 = qrbuf1;
> +
> +try_again:
> +	if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
> +			      MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
> +		return -EINVAL;
> +
> +	stream.next_in = kmsg;
> +	stream.avail_in = kmsg_len;
> +	stream.total_in = 0;
> +	stream.next_out = qrbuf2;
> +	stream.avail_out = QR_BUFFER2_SIZE;
> +	stream.total_out = 0;
> +
> +	if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
> +		return -EINVAL;
> +
> +	if (zlib_deflateEnd(&stream) != 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 = strchr(kmsg, '\n');
> +		if (!kmsg)
> +			return -EINVAL;
> +		/* skip the first \n */
> +		kmsg += 1;
> +		kmsg_len = strlen(kmsg);
> +		goto try_again;
> +	}
> +	*qr_image = qrbuf2;
> +
> +	/* generate qr code image in buffer2 */
> +	return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE,
> +				     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 = 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 = 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 = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
> +	u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
> +	const struct font_desc *font = 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 = DRM_RECT_INIT(0, 0, sb->width, sb->height);
> +
> +	drm_panic_logo_rect(&r_logo, font);
> +
> +	panic_msg_width = get_max_line_len(panic_msg, panic_msg_lines) * font->width;
> +	r_msg = DRM_RECT_INIT(0, 0,
> +			      min(panic_msg_width, sb->width),
> +			      min(panic_msg_lines * font->height, sb->height));
> +
> +	max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
> +
> +	qr_width = drm_panic_get_qr_code(&qr_image);
> +	if (qr_width <= 0)
> +		return -ENOSPC;
> +
> +	qr_canvas_width = qr_width + QR_MARGIN * 2;
> +	scale = 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 = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
> +
> +	v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg)) / 5;
> +
> +	drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
> +	r_qr = 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_qr))
> +		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 = 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 +870,8 @@ static void draw_panic_dispatch(struct drm_scanout_buffer *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 +996,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..1ef56cb07dfb
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_panic_qr.rs
> @@ -0,0 +1,1003 @@
> +// SPDX-License-Identifier: MIT
> +
> +//! This is a simple QR encoder for DRM panic.
> +//!
> +//! It is called from a panic handler, so it should'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 applies the
> +//! first mask (checkerboard). 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 wastes 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 uses 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 the numbers are
> +//! valid URL parameter, so the website can do the reverse, to get the
> +//! binary data.
> +//!
> +//! Inspired by these 3 projects, all under MIT license:
> +//!
> +//! * <https://github.com/kennytm/qrcode-rust>
> +//! * <https://github.com/erwanvivien/fast_qr>
> +//! * <https://github.com/bjguillot/qr>
> +
> +use core::cmp;
> +use kernel::str::CStr;
> +
> +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
> +struct Version(usize);
> +
> +// Generator polynomials for ECC, only those that are needed for low quality.
> +const P7: [u8; 7] = [87, 229, 146, 149, 238, 102, 21];
> +const P10: [u8; 10] = [251, 67, 46, 61, 118, 70, 64, 94, 32, 45];
> +const P15: [u8; 15] = [
> +    8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105,
> +];
> +const P18: [u8; 18] = [
> +    215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153,
> +];
> +const P20: [u8; 20] = [
> +    17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190,
> +];
> +const P22: [u8; 22] = [
> +    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] = [
> +    229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117,
> +    232, 87, 96, 227, 21,
> +];
> +const P26: [u8; 26] = [
> +    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] = [
> +    168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87,
> +    42, 195, 212, 119, 242, 37, 9, 123,
> +];
> +const P30: [u8; 30] = [
> +    41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193,
> +    224, 130, 156, 37, 251, 216, 238, 40, 192, 180,
> +];
> +
> +/// QR Code parameters 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] = [
> +    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 = 30;
> +const MAX_BLK_SIZE: usize = 123;
> +
> +/// Position of the alignment pattern grid.
> +const ALIGNMENT_PATTERNS: [&[u8]; 40] = [
> +    &[],
> +    &[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] = [
> +    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 quality ECC.
> +const FORMAT_INFOS_QR_L: [u16; 8] = [
> +    0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976,
> +];
> +
> +impl Version {
> +    /// Returns the smallest QR version than can hold these segments.
> +    fn from_segments(segments: &[&Segment<'_>]) -> Option<Version> {
> +        for v in (1..=40).map(|k| Version(k)) {
> +            if v.max_data() * 8 >= 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 >= Version(7) {
> +            VERSION_INFORMATION[self.0 - 7]
> +        } else {
> +            0
> +        }
> +    }
> +}
> +
> +/// Exponential table for Galois Field GF(256).
> +const EXP_TABLE: [u8; 256] = [
> +    1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117,
> +    234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 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, 240, 253, 231, 211, 187,
> +    107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 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, 66, 132, 21, 42, 84, 168,
> +    77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198,
> +    145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 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] = [
> +    175, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 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, 34, 136, 54, 208, 148,
> +    206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126,
> +    110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172,
> +    115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 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, 171, 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 = 0;
> +const MODE_NUMERIC: u16 = 1;
> +const MODE_BINARY: u16 = 4;
> +/// Padding bytes.
> +const PADDING: [u8; 2] = [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 = cmp::min(13, data.len() * 8 - offset);
> +        let byte_off = offset / 8;
> +        let bit_off = offset % 8;
> +        // `b` is 20 at max (`bit_off` <= 7 and `size` <= 13).
> +        let b = (bit_off + size) as u16;
> +
> +        let first_byte = (data[byte_off] << bit_off >> bit_off) as u16;
> +
> +        let number = match b {
> +            0..=8 => first_byte >> (8 - b),
> +            9..=16 => (first_byte << (b - 8)) + (data[byte_off + 1] >> (16 - b)) as u16,
> +            _ => {
> +                (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] = [0, 4, 7, 10];
> +const POW10: [u16; 4] = [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(_) => (MODE_BINARY, 4),
> +            Segment::Numeric(_) => (MODE_NUMERIC, 4),
> +        }
> +    }
> +
> +    // Returns the size of the length field in bits, depending on QR Version.
> +    fn length_bits_count(&self, version: Version) -> usize {
> +        let Version(v) = version;
> +        match self {
> +            Segment::Binary(_) => match v {
> +                1..=9 => 8,
> +                _ => 16,
> +            },
> +            Segment::Numeric(_) => match v {
> +                1..=9 => 10,
> +                10..=26 => 12,
> +                _ => 14,
> +            },
> +        }
> +    }
> +
> +    // Number of characters in the segment.
> +    fn character_count(&self) -> usize {
> +        match self {
> +            Segment::Binary(data) => data.len(),
> +            Segment::Numeric(data) => {
> +                let data_bits = data.len() * 8;
> +                let last_chars = match data_bits % 13 {
> +                    1 => 1,
> +                    k => (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 = match self {
> +            Segment::Binary(data) => data.len() * 8,
> +            Segment::Numeric(_) => {
> +                let digits = 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 = (u16, usize);
> +
> +    fn next(&mut self) -> Option<Self::Item> {
> +        match self.segment {
> +            Segment::Binary(data) => {
> +                if self.offset < data.len() {
> +                    let byte = data[self.offset] as u16;
> +                    self.offset += 1;
> +                    Some((byte, 8))
> +                } else {
> +                    None
> +                }
> +            }
> +            Segment::Numeric(data) => {
> +                if self.carry_len == 3 {
> +                    let out = (self.carry, NUM_CHARS_BITS[self.carry_len]);
> +                    self.carry_len = 0;
> +                    self.carry = 0;
> +                    Some(out)
> +                } else if let Some((bits, size)) = get_next_13b(data, self.offset) {
> +                    self.offset += size;
> +                    let new_chars = match size {
> +                        1 => 1,
> +                        k => (k + 1) / 3,
> +                    };
> +                    if self.carry_len + new_chars > 3 {
> +                        self.carry_len = new_chars + self.carry_len - 3;
> +                        let out = (
> +                            self.carry * POW10[new_chars - self.carry_len]
> +                                + bits / POW10[self.carry_len],
> +                            NUM_CHARS_BITS[3],
> +                        );
> +                        self.carry = bits % POW10[self.carry_len];
> +                        Some(out)
> +                    } else {
> +                        let out = (
> +                            self.carry * POW10[new_chars] + bits,
> +                            NUM_CHARS_BITS[self.carry_len + new_chars],
> +                        );
> +                        self.carry_len = 0;
> +                        Some(out)
> +                    }
> +                } else if self.carry_len > 0 {
> +                    let out = (self.carry, NUM_CHARS_BITS[self.carry_len]);
> +                    self.carry_len = 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,
> +}
> +
> +/// Data to be put in the QR code, with correct segment encoding, padding, and
> +/// Error Code Correction.
> +impl EncodedMsg<'_> {
> +    fn new<'a, 'b>(segments: &[&Segment<'b>], data: &'a mut [u8]) -> Option<EncodedMsg<'a>> {
> +        let version = Version::from_segments(segments)?;
> +        let ec_size = version.ec_size();
> +        let g1_blocks = version.g1_blocks();
> +        let g2_blocks = version.g2_blocks();
> +        let g1_blk_size = version.g1_blk_size();
> +        let g2_blk_size = g1_blk_size + 1;
> +        let poly = version.poly();
> +
> +        // clear the output.
> +        data.fill(0);
> +
> +        let mut em = 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) = bits;
> +        let byte_off = *offset / 8;
> +        let bit_off = *offset % 8;
> +        let b = bit_off + len_bits;
> +
> +        match (bit_off, b) {
> +            (0, 0..=8) => {
> +                self.data[byte_off] = (number << (8 - b)) as u8;
> +            }
> +            (0, _) => {
> +                self.data[byte_off] = (number >> (b - 8)) as u8;
> +                self.data[byte_off + 1] = (number << (16 - b)) as u8;
> +            }
> +            (_, 0..=8) => {
> +                self.data[byte_off] |= (number << (8 - b)) as u8;
> +            }
> +            (_, 9..=16) => {
> +                self.data[byte_off] |= (number >> (b - 8)) as u8;
> +                self.data[byte_off + 1] = (number << (16 - b)) as u8;
> +            }
> +            _ => {
> +                self.data[byte_off] |= (number >> (b - 8)) as u8;
> +                self.data[byte_off + 1] = (number >> (b - 16)) as u8;
> +                self.data[byte_off + 2] = (number << (24 - b)) as u8;
> +            }
> +        }
> +        *offset += len_bits;
> +    }
> +
> +    fn add_segments(&mut self, segments: &[&Segment<'_>]) {
> +        let mut offset: usize = 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 = (offset + 7) / 8;
> +        for i in pad_offset..self.version.max_data() {
> +            self.data[i] = PADDING[(i & 1) ^ (pad_offset & 1)];
> +        }
> +    }
> +
> +    fn error_code_for_blocks(&mut self, offset: usize, size: usize, ec_offset: usize) {
> +        let mut tmp: [u8; MAX_BLK_SIZE + MAX_EC_SIZE] = [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 = tmp[i] as usize;
> +            if lead_coeff == 0 {
> +                continue;
> +            }
> +            let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]);
> +            for (u, &v) in tmp[i + 1..].iter_mut().zip(self.poly.iter()) {
> +                *u ^= 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 = 0;
> +        let mut ec_offset = 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 += self.g1_blk_size;
> +            ec_offset += self.ec_size;
> +        }
> +        for _ in 0..self.g2_blocks {
> +            self.error_code_for_blocks(offset, self.g2_blk_size, ec_offset);
> +            offset += self.g2_blk_size;
> +            ec_offset += 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,
> +        }
> +    }
> +}
> +
> +/// Iterator, to retrieve the data in the interleaved order needed by QR code.
> +struct EncodedMsgIterator<'a> {
> +    em: &'a EncodedMsg<'a>,
> +    offset: usize,
> +}
> +
> +impl Iterator for EncodedMsgIterator<'_> {
> +    type Item = u8;
> +
> +    // Send the bytes in interleaved mode, first byte of first block of group1,
> +    // then first byte of second block of group1, ...
> +    fn next(&mut self) -> Option<Self::Item> {
> +        let em = self.em;
> +        let blocks = em.g1_blocks + em.g2_blocks;
> +        let g1_end = em.g1_blocks * em.g1_blk_size;
> +        let g2_end = g1_end + em.g2_blocks * em.g2_blk_size;
> +        let ec_end = g2_end + em.ec_size * blocks;
> +
> +        if self.offset >= ec_end {
> +            return None;
> +        }
> +
> +        let offset = if self.offset < em.g1_blk_size * blocks {
> +            // group1 and group2 interleaved
> +            let blk = self.offset % blocks;
> +            let blk_off = 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 = 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 = self.offset - g2_end;
> +            let blk = ec_offset % blocks;
> +            let blk_off = ec_offset / blocks;
> +
> +            g2_end + blk * em.ec_size + blk_off
> +        };
> +        self.offset += 1;
> +        Some(em.data[offset])
> +    }
> +}
> +
> +/// A QR code image, encoded as a linear binary framebuffer.
> +/// 1 bit per module (pixel), each new line start at next byte boundary.
> +/// 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]) -> QrImage<'a> {
> +        let width = em.version.width();
> +        let stride = (width + 7) / 8;
> +        let data = qrdata;
> +
> +        let mut qr_image = 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 = y as usize * self.stride as usize + x as usize / 8;
> +        let mut v = self.data[off];
> +        v |= 0x80 >> (x % 8);
> +        self.data[off] = v;
> +    }
> +
> +    // Invert a module color.
> +    fn xor(&mut self, x: u8, y: u8) {
> +        let off = y as usize * self.stride as usize + x as usize / 8;
> +        self.data[off] ^= 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 = self.width - 8;
> +        (x < 8 && y < 8) || (x < 8 && y >= end) || (x >= end && y < 8)
> +    }
> +
> +    // Alignment pattern: 5x5 squares in a grid.
> +    fn draw_alignments(&mut self) {
> +        let positions = 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 = self.version.alignment_pattern();
> +        for &ax in positions.iter() {
> +            for &ay in positions.iter() {
> +                if self.is_finder(ax, ay) {
> +                    continue;
> +                }
> +                if x >= ax - 2 && x <= ax + 2 && y >= ay - 2 && y <= ay + 2 {
> +                    return true;
> +                }
> +            }
> +        }
> +        false
> +    }
> +
> +    // Timing pattern: 2 dotted line between the finder patterns.
> +    fn draw_timing_patterns(&mut self) {
> +        let end = 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 == 6 || y == 6
> +    }
> +
> +    // Mask info: 15 bits around the finders, written twice for redundancy.
> +    fn draw_maskinfo(&mut self) {
> +        let info: u16 = FORMAT_INFOS_QR_L[0];
> +        let mut skip = 0;
> +
> +        for k in 0..7 {
> +            if k == 6 {
> +                skip = 1;
> +            }
> +            if info & (1 << (14 - k)) == 0 {
> +                self.set(k + skip, 8);
> +                self.set(8, self.width - 1 - k);
> +            }
> +        }
> +        skip = 0;
> +        for k in 0..8 {
> +            if k == 2 {
> +                skip = 1;
> +            }
> +            if info & (1 << (7 - k)) == 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 = self.width - 8;
> +        // Count the dark module as mask info.
> +        (x <= 8 && y == 8) || (y <= 8 && x == 8) || (x == 8 && y >= end) || (x >= end && y == 8)
> +    }
> +
> +    // Version info: 18bits written twice, close to the finders.
> +    fn draw_version_info(&mut self) {
> +        let vinfo = self.version.version_info();
> +        let pos = self.width - 11;
> +
> +        if vinfo != 0 {
> +            for x in 0..3 {
> +                for y in 0..6 {
> +                    if vinfo & (1 << (x + y * 3)) == 0 {
> +                        self.set(x + pos, y);
> +                        self.set(y, x + pos);
> +                    }
> +                }
> +            }
> +        }
> +    }
> +
> +    fn is_version_info(&self, x: u8, y: u8) -> bool {
> +        let vinfo = self.version.version_info();
> +        let pos = self.width - 11;
> +
> +        vinfo != 0 && ((x >= pos && x < pos + 3 && y < 6) || (y >= pos && y < pos + 3 && x < 6))
> +    }
> +
> +    // Returns true if the module is reserved (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)
> +    }
> +
> +    // Last module to draw, at bottom left corner.
> +    fn is_last(&self, x: u8, y: u8) -> bool {
> +        x == 0 && y == self.width - 1
> +    }
> +
> +    // Move to the next module 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 = if x <= 6 { x + 1 } else { x };
> +        let column_type = (self.width - x_adj) % 4;
> +
> +        match column_type {
> +            2 if y > 0 => (x + 1, y - 1),
> +            0 if y < self.width - 1 => (x + 1, y + 1),
> +            0 | 2 if x == 7 => (x - 2, y),
> +            _ => (x - 1, y),
> +        }
> +    }
> +
> +    // Find next module that can hold data.
> +    fn next_available(&self, x: u8, y: u8) -> (u8, u8) {
> +        let (mut x, mut y) = self.next(x, y);
> +        while self.is_reserved(x, y) && !self.is_last(x, y) {
> +            (x, y) = self.next(x, y);
> +        }
> +        (x, y)
> +    }
> +
> +    fn draw_data(&mut self, data: impl Iterator<Item = u8>) {
> +        let (mut x, mut y) = (self.width - 1, self.width - 1);
> +        for byte in data {
> +            for s in 0..8 {
> +                if byte & (0x80 >> s) == 0 {
> +                    self.set(x, y);
> +                }
> +                (x, y) = self.next_available(x, y);
> +            }
> +        }
> +        // Set the remaining modules (0, 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) = self.next(x, y);
> +        }
> +    }
> +
> +    // Apply checkerboard 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 == 0 && !self.is_reserved(x, y) {
> +                    self.xor(x, y);
> +                }
> +            }
> +        }
> +    }
> +
> +    // Draw the QR code with the provided data iterator.
> +    fn draw_all(&mut self, data: impl Iterator<Item = u8>) {
> +        // 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();
> +    }
> +}
> +
> +/// C entry point for the rust QR Code generator.
> +///
> +/// Write the QR code image in the data buffer, and return the QR code width,
> +/// 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 segment.
> +/// * `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, must be less
> +///    than data_size.
> +/// * `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 bytes
> +///    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.
> +/// * `tmp` must be valid for reading and writing for `tmp_size` bytes.
> +///
> +/// They must remain valid for the duration of the function call.
> +
> +#[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 < 4071 || tmp_size < 3706 || data_len > data_size {
> +        return 0;
> +    }
> +    // SAFETY: The caller ensures that `data` is a valid pointer for reading and
> +    // writing `data_size` bytes.
> +    let data_slice: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(data, data_size) };
> +    // SAFETY: The caller ensures that `tmp` is a valid pointer for reading and
> +    // writing `tmp_size` bytes.
> +    let tmp_slice: &mut [u8] = 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 => 0,
> +            Some(em) => {
> +                let qr_image = QrImage::new(&em, data_slice);
> +                qr_image.width
> +            }
> +        }
> +    } else {
> +        // SAFETY: The caller ensures that `url` is a valid pointer to a
> +        // nul-terminated string.
> +        let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) };
> +        let segments = &[
> +            &Segment::Binary(url_cstr.as_bytes()),
> +            &Segment::Numeric(&data_slice[0..data_len]),
> +        ];
> +        match EncodedMsg::new(segments, tmp_slice) {
> +            None => 0,
> +            Some(em) => {
> +                let qr_image = QrImage::new(&em, data_slice);
> +                qr_image.width
> +            }
> +        }
> +    }
> +}
> +
> +/// Returns the maximum data size that can fit in a QR code of this version.
> +/// * `version`: QR code version, between 1-40.
> +/// * `url_len`: Length of the URL.
> +///
> +/// * If `url_len` > 0, remove the 2 segments header/length and also count the
> +/// conversion to numeric segments.
> +/// * If `url_len` = 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 = Version(version as usize).max_data();
> +
> +    if url_len > 0 {
> +        // Binary segment (URL) 4 + 16 bits, numeric segment (kmsg) 4 + 12 bits => 5 bytes.
> +        if url_len + 5 >= max_data {
> +            0
> +        } else {
> +            let max = max_data - url_len - 5;
> +            (max * 39) / 40
> +        }
> +    } else {
> +        // Remove 3 bytes for the binary segment (header 4 bits, length 16 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 {
>   
>   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);
>   
>   #else
>   
>   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) {}
>   
>   #endif
>   

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ