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 for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 2 Nov 2015 10:56:00 -0800
From:	Laura Abbott <labbott@...hat.com>
To:	Murtaza Alexandru <alexandru.murtaza@...el.com>,
	linux-kernel@...r.kernel.org
Cc:	teodora.baluta@...el.com, levex@...ux.com, dirk.hohndel@...el.com,
	h.peter.anvin@...el.com
Subject: Re: [RFC][PATCH] Oops messages transfer using QR codes

On 10/30/2015 08:03 AM, Murtaza Alexandru wrote:
> This patch is continued from this previous RFC:
> https://lkml.org/lkml/2014/3/17/525
>
> If the config option for this feature is enabled, then when an Oops is
> in progress, the printk() calls' strings are buffered. When the Oops
> finishes, the buffer is split into multiple packets, compressed, encoded
> into QRs and then displayed in console. The compression is done using
> DEFLATE (from lib/zlib/). The user can issue commands writing to the sysfs.
> For scanning and automatic reporting Bug Koops (Android application) is
> needed [0].
>
> Problems with the previous patch:
> 1. Even if the specifications says you can encode up to 2980 bytes into a
> QR code, in reality, you can only encode about 200 bytes if you want to
> scan it with a smartphone. Using DEFLATE, we can compress 300 bytes so that
> the output is less than 200 bytes almost all the time. From what I saw, an
> Oops message is on average about 1.7kb. So the conclusion is: one QR is not
> enough.
>
> 2. If there are multiple Oopses occurring on different CPUs there is no way
> to draw both QRs. Even if we would take not to overlap them, we would be
> left with even less space to draw which results in less info we are able
> to send.
>
> 3. Another problem in the previous RFC was that there is no way (or at
> least I didn't figured it out) of knowing if what we write in the
> framebuffer actually gets to the screen and also there is no simple way
> to guarantee that what we draw is always on top of the text.
>
>
> Proposed solution for the above stated problems:
> When we finish capturing an Oops, split the message into multiple chunks,
> compress them and encode into some format so we will be able to get the
> initial message again no matter in what order we scan the QRs. For drawing,
> we are going to make a kthread and continuously draw. This will guarantee
> the QR is always on top. Also this will enable rendering an "animation".
>
> The way data is encoded into a packet is described here [1] but TL;DR:
> We are 100% sure the QR code is scanned correctly so we don't need error
> correction, we are only in a 1-way "communication" so we have to run the
> animation indefinitely anyway. The things we care about is being able
> to distinguish between different messages, for one message to be able
> to reorder the packets and sanity check of the message (check if the user
> is not scanning normal QRs). For more detailed information and explanation
> please see [1].
>
> For this patch to work properly you need to have framebuffer enabled.
> Anyway, if you do not have one, ASCII art will be used as a fallback.
>
>  From BugKoops to QR module communication we have to use the user (tell him
> what to do). For user to QR module we use sysfs. Current implementation
> supports stop, pause, resume, clear queue and print packets ids, print
> messages ids, delete packet, delete message, which are all useful in
> case you want to speed up the scanning process a little bit.
>
> Current issues:
>   * screen flush and console acquisition is forced with a printk
>   * not tested for Oops messages issued from several CPUs
>   * all values and data presented here are empirical and finer tuning should
>     be done
>
> I hope this will give an impulse to the community to search a way
> and extend this for panics too, which would be much more useful.
>

I'm glad to see someone continuing on this work. For the next version, can
you break it up for ease of review? I'd say put the lib/qr/ addition as one
patch and then the print_oops as a second patch.


> <snip>
> diff --git a/include/linux/qrencode.h b/include/linux/qrencode.h
> new file mode 100644
> index 0000000..112c4bf
> --- /dev/null
> +++ b/include/linux/qrencode.h
> @@ -0,0 +1,504 @@
> +/**
> + * qrencode - QR Code encoder
> + *
> + * Copyright (C) 2006-2012 Kentaro Fukuchi <kentaro@...uchi.org>
> + * This library is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +/** \mainpage
> + * Libqrencode is a library for encoding data in a QR Code symbol, a kind of 2D
> + * symbology.
> + *
> + * \section encoding Encoding
> + *
> + * There are two methods to encode data: <b>encoding a string/data</b> or
> + * <b>encoding a structured data</b>.
> + *
> + * \subsection encoding-string Encoding a string/data
> + * You can encode a string by calling qrcode_encode_string().
> + * The given string is parsed automatically and encoded. If you want to encode
> + * data that can be represented as a C string style (NUL terminated), you can
> + * simply use this way.
> + *
> + * If the input data contains Kanji (Shift-JIS) characters and you want to
> + * encode them as Kanji in QR Code, you should give QR_MODE_KANJI as a hint.
> + * Otherwise, all of non-alphanumeric characters are encoded as 8 bit data.
> + * If you want to encode a whole string in 8 bit mode, you can use
> + * qrcode_encode_string_8bit() instead.
> + *
> + * Please note that a C string can not contain NUL characters. If your data
> + * contains NUL, you must use qrcode_encode_data().
> + *
> + * \subsection encoding-input Encoding a structured data
> + * You can construct a structured input data manually. If the structure of the
> + * input data is known, you can use this way.
> + * At first, create a ::qrinput object by qrinput_new(). Then add input data
> + * to the qrinput object by qrinput_append(). Finally call qrcode_encode_input()
> + * to encode the qrinput data.
> + * You can reuse the qrinput data again to encode it in other symbols with
> + * different parameters.
> + *
> + * \section result Result
> + * The encoded symbol is resulted as a ::qrcode object. It will contain
> + * its version number, width of the symbol and an array represents the symbol.
> + * See ::qrcode for the details. You can free the object by qrcode_free().
> + *
> + * Please note that the version of the result may be larger than specified.
> + * In such cases, the input data would be too large to be encoded in a
> + * symbol of the specified version.
> + *
> + * \section structured Structured append
> + * Libqrencode can generate "Structured-appended" symbols that enables to split
> + * a large data set into mulitple QR codes. A QR code reader concatenates
> + * multiple QR code symbols into a string.
> + * Just like qrcode_encode_string(), you can use
> + * qrcode_encode_string_structured() to generate structured-appended symbols.
> + * This functions returns an instance of ::qrcode_list. The returned list is a
> + * singly-linked list of qrcode: you
> + * can retrieve each QR code in this way:
> + *
> + * \code
> + * qrcode_list *qrcodes;
> + * qrcode_list *entry;
> + * qrcode *qrcode;
> + *
> + * qrcodes = qrcode_encode_string_structured(...);
> + * entry = qrcodes;
> + * while(entry != NULL) {
> + *     qrcode = entry->code;
> + *     // do something
> + *     entry = entry->next;
> + * }
> + * qrcode_list_free(entry);
> + * \endcode
> + *
> + * Instead of using auto-parsing functions, you can construct your own
> + * structured input. At first, instantiate an object of ::qrinput_struct
> + * by calling qrinput_struct_new(). This object can hold multiple ::qrinput,
> + * and one QR code is generated for a ::qrinput.
> + * qrinput_struct_append_input() appends a ::qrinput to a ::qrinput_struct
> + * object. In order to generate structured-appended symbols, it is required to
> + * embed headers to each symbol. You can use
> + * qrinput_struct_insert_structured_append_headers() to insert appropriate
> + * headers to each symbol. You should call this function just once before
> + * encoding symbols.
> + */
> +
> +#ifndef __QRENCODE_H__
> +#define __QRENCODE_H__
> +
> +#if defined(__cplusplus)
> +extern "C" {
> +#endif

Go ahead and drop any __cplusplus wrappers.

> <snip>

> --- a/kernel/panic.c
> +++ b/kernel/panic.c
> @@ -24,6 +24,8 @@
>   #include <linux/init.h>
>   #include <linux/nmi.h>
>
> +#include <linux/print_oops.h>
> +
>   #define PANIC_TIMER_STEP 100
>   #define PANIC_BLINK_SPD 18
>
> @@ -401,6 +403,9 @@ void print_oops_end_marker(void)
>   {
>   	init_oops_id();
>   	pr_warn("---[ end trace %016llx ]---\n", (unsigned long long)oops_id);
> +#ifdef CONFIG_QR_OOPS
> +	print_qr_err();
> +#endif

The preferred style is to not have #ifdef in functions if it can be
avoided. #ifdef in the header file for print_qr_err would be better.

>   }
>
>   /*
> diff --git a/kernel/print_oops.c b/kernel/print_oops.c
> new file mode 100644
> index 0000000..e367228
> --- /dev/null
> +++ b/kernel/print_oops.c
> @@ -0,0 +1,693 @@
> +/*
> + *
> + * Copyright (C) 2015 Murtaza Alexandru <alexandru.murtaza@...el.com>
> + * Copyright (C) 2014 Teodora Baluta <teobaluta@...il.com>
> + * Copyright (C) 2014 Levente Kurusa <levex@...ux.com>
> + *
> + * This is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This code is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + *
> + */
> +#include <linux/print_oops.h>
> +#include <linux/kdebug.h>
> +#include <linux/bug.h>
> +#include <linux/qrencode.h>
> +#include <linux/fb.h>
> +#include <linux/zlib.h>
> +#include <linux/vmalloc.h>
> +#include <linux/semaphore.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/kthread.h>
> +#include <linux/time.h>
> +#include <linux/list.h>
> +#include <linux/jiffies.h>
> +
> +#define COMPR_LEVEL 9
> +#define QQQ_WHITE 0x0F
> +#define QQQ_BLACK 0x00
> +
> +static int qr_oops_cmd;
> +core_param(qr_oops_cmd, qr_oops_cmd, int, 0644);
> +
> +static int qr_oops_param0;
> +core_param(qr_oops_param0, qr_oops_param0, int, 0644);
> +
> +static int qr_oops_param1;
> +core_param(qr_oops_param1, qr_oops_param1, int, 0644);
> +
> +static char qr_buffer[MESSAGE_BUFSIZE];
> +static int buf_pos;
> +
> +static DEFINE_MUTEX(compr_mutex);
> +static DEFINE_MUTEX(qr_list_mutex);


I gave it a quick test with just a 'echo c > /proc/sysrq-trigger'
and got a scheduling while atomic warning in addition to not seeing a
QR code. I don't think waking up a thread on panic is the correct approach
here. Can you elaborate more on what problem you were trying to solve by
adding a thread?

> +static struct z_stream_s stream;
> +
> +static int compr_init(void)
> +{
> +	size_t size = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
> +			zlib_inflate_workspacesize());
> +
> +	stream.workspace = vmalloc(size);
> +	if (!stream.workspace)
> +		return -ENOMEM;
> +	return 0;
> +}
> +
> +static void compr_exit(void)
> +{
> +	vfree(stream.workspace);
> +}
> +
> +static int compress(void *in, void *out, size_t inlen, size_t outlen)
> +{
> +	int ret;
> +
> +	ret = compr_init();
> +	if (ret != 0)
> +		goto error;
> +
> +	mutex_lock(&compr_mutex);
> +	ret = zlib_deflateInit(&stream, COMPR_LEVEL);
> +	if (ret != Z_OK) {
> +		pr_emerg("qr_compress: zlib_deflateInit failed with ret = %d\n",
> +			 ret);
> +		goto error;
> +	}
> +
> +	stream.next_in = in;
> +	stream.avail_in = inlen;
> +	stream.total_in = 0;
> +	stream.next_out = out;
> +	stream.avail_out = outlen;
> +	stream.total_out = 0;
> +
> +	ret = zlib_deflate(&stream, Z_FINISH);
> +	if (ret != Z_STREAM_END) {
> +		pr_emerg("qr_compress: zlib_deflate failed with ret = %d\n",
> +			 ret);
> +		goto error;
> +	}
> +
> +	ret = zlib_deflateEnd(&stream);
> +	if (ret != Z_OK) {
> +		pr_emerg("qr_compress: zlib_deflateEnd failed with ret = %d\n",
> +			 ret);
> +		goto error;
> +	}
> +
> +	if (stream.total_out >= stream.total_in)
> +		pr_emerg("qr_compress: total_out > total_in\n");
> +
> +	ret = stream.total_out;
> +error:
> +	mutex_unlock(&compr_mutex);
> +	return ret;
> +}
> +
> +static inline int compute_w(struct fb_info *info, int qrw)
> +{
> +	int xres  = info->var.xres;
> +	int yres  = info->var.yres;
> +	int minxy = (xres < yres) ? xres : yres;
> +	int ret = minxy - minxy / 4;
> +
> +	return ret / qrw;
> +}
> +
> +static inline void draw_qr(struct fb_info *info, struct qrcode *qr,
> +			   int pos_x, int pos_y,
> +			   int cell_width, int cell_height,
> +			   int border)
> +{
> +	int i, j;
> +	int is_black;
> +	struct fb_fillrect rect;
> +
> +	rect.width = cell_width;
> +	rect.height = cell_height;
> +	rect.rop = 0;
> +
> +	if (border) {
> +		/* Print borders: */
> +		rect.color = QQQ_WHITE;
> +		for (i = 0; i < qr->width + 2; i++) {
> +			/* Top */
> +			rect.dx = 0 + pos_x;
> +			rect.dy = i * cell_height + pos_y;
> +			cfb_fillrect(info, &rect);
> +
> +			/* Bottom */
> +			rect.dx = (qr->width + 1) * cell_width + pos_x;
> +			rect.dy = i * cell_height + pos_y;
> +			cfb_fillrect(info, &rect);
> +
> +			/* Left */
> +			rect.dx = i * cell_width + pos_x;
> +			rect.dy = pos_y;
> +			cfb_fillrect(info, &rect);
> +
> +			/* Right */
> +			rect.dx = i * cell_width + pos_x;
> +			rect.dy = (qr->width + 1) * cell_height + pos_y;
> +			cfb_fillrect(info, &rect);
> +		}
> +	}
> +
> +	/* Print actual QR matrix: */
> +	for (i = 0; i < qr->width; i++) {
> +		for (j = 0; j < qr->width; j++) {
> +			rect.dx = (j + 1) * cell_width + pos_x;
> +			rect.dy = (i + 1) * cell_height + pos_y;
> +			is_black = qr->data[i * qr->width + j] & 1;
> +			rect.color = is_black ? QQQ_BLACK : QQQ_WHITE;
> +			cfb_fillrect(info, &rect);
> +		}
> +	}
> +}
> +
> +#define ASCII_BLACK " "
> +#define ASCII_BLOCK "%c", 219
> +#define ASCII_HALFBLOCK_TOP	"%c", 223
> +#define ASCII_HALFBLOCK_BOTTOM	"%c", 220
> +
> +static inline int qr_is_black(struct qrcode *qr, int x, int y)
> +{
> +	if (x < 0 || y < 0 || x >= qr->width || y >= qr->width)
> +		return 0;
> +	return qr->data[x * qr->width + y] & 1;
> +}
> +
> +static inline void draw_ascii_qr(struct qrcode *qr)
> +{
> +	int i, j;
> +	int up, down;
> +
> +	/* Print actual QR matrix: */
> +	for (i = -1; i < qr->width + 1; i += 2) {
> +		for (j = -1; j < qr->width + 1; j++) {
> +			up = 1 - qr_is_black(qr, i, j);
> +			down = 1 - qr_is_black(qr, i + 1, j);
> +			if (up)
> +				if (down)
> +					printk(ASCII_BLOCK);
> +				else
> +					printk(ASCII_HALFBLOCK_TOP);
> +			else
> +				if (down)
> +					printk(ASCII_HALFBLOCK_BOTTOM);
> +				else
> +					printk(ASCII_BLACK);
> +		}
> +		printk("\n");
> +	}
> +}
> +
> +struct qr_chunk {
> +	struct list_head list;
> +
> +	struct qrcode *qr;
> +	int message_id;
> +	int packet_id;
> +};
> +
> +static LIST_HEAD(qr_list_head);
> +
> +static struct task_struct *qr_thread;
> +
> +#define BUFFER_SIZE	(4 * 1024)
> +
> +static inline void qr_list_delete(struct qr_chunk *element)
> +{
> +	list_del(&element->list);
> +	vfree(element);
> +}
> +
> +static void qr_list_delete_message(int message_id)
> +{
> +	struct list_head *it, *n;
> +	struct qr_chunk *qr_entry;
> +
> +	mutex_lock(&qr_list_mutex);
> +	list_for_each_safe(it, n, &qr_list_head) {
> +		qr_entry = list_entry(it, struct qr_chunk, list);
> +		if (qr_entry->message_id == message_id)
> +			qr_list_delete(qr_entry);
> +	}
> +	mutex_unlock(&qr_list_mutex);
> +}
> +
> +static void qr_list_clear(void)
> +{
> +	struct list_head *it, *n;
> +
> +	mutex_lock(&qr_list_mutex);
> +	list_for_each_safe(it, n, &qr_list_head)
> +		qr_list_delete(list_entry(it, struct qr_chunk, list));
> +	mutex_unlock(&qr_list_mutex);
> +}
> +
> +static void qr_list_delete_packet(int message_id, int packet_id)
> +{
> +	struct list_head *it, *n;
> +	struct qr_chunk *qr_entry;
> +
> +	mutex_lock(&qr_list_mutex);
> +	list_for_each_safe(it, n, &qr_list_head) {
> +		qr_entry = list_entry(it, struct qr_chunk, list);
> +		if (qr_entry->message_id == message_id &&
> +		    qr_entry->packet_id == packet_id)
> +			qr_list_delete(qr_entry);
> +	}
> +	mutex_unlock(&qr_list_mutex);
> +}
> +
> +#define BK1_MAGIC_FIRSTBYTE 222
> +#define BK1_MAGIC_SECONDBYTE 173
> +
> +#define BK1_ENCODE_NONE 0
> +#define BK1_ENCODE_DEFLATE 1
> +
> +#define BK1_VERSION 0
> +
> +static void make_bk1_packet(char *start, int len, int message_id,
> +			    int packet_id, int packet_count)
> +{
> +	struct qr_chunk *element;
> +	int header_size;
> +	ssize_t packet_len;
> +	char compr_packet_buffer[BUFFER_SIZE];
> +	char checksum;
> +
> +	element = vmalloc(sizeof(*element));
> +	element->message_id = message_id;
> +	element->packet_id = packet_id;
> +
> +	header_size = 0;
> +
> +	checksum = BK1_VERSION ^ message_id ^ packet_count ^ packet_id ^
> +		   BK1_ENCODE_DEFLATE;
> +
> +	compr_packet_buffer[header_size] = BK1_MAGIC_FIRSTBYTE;
> +	++header_size;
> +	compr_packet_buffer[header_size] = BK1_MAGIC_SECONDBYTE;
> +	++header_size;
> +	compr_packet_buffer[header_size] = BK1_VERSION;
> +	++header_size;
> +	compr_packet_buffer[header_size] = message_id;
> +	++header_size;
> +	compr_packet_buffer[header_size] = packet_count;
> +	++header_size;
> +	compr_packet_buffer[header_size] = packet_id;
> +	++header_size;
> +	compr_packet_buffer[header_size] = BK1_ENCODE_DEFLATE;
> +	++header_size;
> +	compr_packet_buffer[header_size] = checksum;
> +	++header_size;
> +	compr_packet_buffer[header_size] = (len >> 8) & 0xFF;
> +	++header_size;
> +	compr_packet_buffer[header_size] = len & 0xFF;
> +	++header_size;
> +
> +	packet_len = compress(start, compr_packet_buffer + header_size, len,
> +			      BUFFER_SIZE - header_size);
> +	if (packet_len < 0) {
> +		pr_emerg("QR: Compression of QR code failed packet_len=%zd\n",
> +			 packet_len);
> +		goto ERROR;
> +	}
> +	compr_exit();
> +
> +	packet_len += header_size;
> +
> +	element->qr = qrcode_encode_data(packet_len, compr_packet_buffer, 0,
> +					 QR_ECLEVEL_H);
> +	if (!element->qr) {
> +		pr_emerg("QR: Failed to encode data as a QR code!\n");
> +		goto ERROR;
> +	}
> +
> +	mutex_lock(&qr_list_mutex);
> +	list_add(&element->list, &qr_list_head);
> +	mutex_unlock(&qr_list_mutex);
> +
> +	return;
> +ERROR:
> +	pr_emerg("QR: Failed to make QR message packet!\n");
> +}
> +
> +struct qr_chunk *tar_slow, *tar_fast, *tar_current;
> +int tar_step;
> +
> +static struct qr_chunk *list_circular_next_entry(struct qr_chunk *it)
> +{
> +	it = list_next_entry(it, list);
> +	if (it == &qr_list_head)
> +		it = list_next_entry(it, list);
> +
> +	return it;
> +}
> +
> +static inline void tar_strategy_next_step(void)
> +{
> +	++tar_step;
> +	if (tar_step == 4)
> +		tar_step = 0;
> +
> +	if (tar_step == 0) {
> +		mutex_lock(&qr_list_mutex);
> +		tar_slow = list_circular_next_entry(tar_slow);
> +		mutex_unlock(&qr_list_mutex);
> +		tar_current = tar_slow;
> +	} else if (tar_step == 1) {
> +		tar_current = tar_slow;
> +	} else if (tar_step == 2) {
> +		mutex_lock(&qr_list_mutex);
> +		tar_fast = list_circular_next_entry(tar_fast);
> +		mutex_unlock(&qr_list_mutex);
> +		tar_current = tar_fast;
> +	} else if (tar_step == 3) {
> +		mutex_lock(&qr_list_mutex);
> +		tar_fast = list_circular_next_entry(tar_fast);
> +		mutex_unlock(&qr_list_mutex);
> +		tar_current = tar_fast;
> +	}
> +}
> +
> +static inline void tar_strategy_init(void)
> +{
> +	tar_step = 0;
> +	tar_slow = list_first_entry(&qr_list_head, struct qr_chunk, list);
> +	tar_fast = list_first_entry(&qr_list_head, struct qr_chunk, list);
> +	tar_current = list_first_entry(&qr_list_head, struct qr_chunk, list);
> +}
> +
> +static inline struct qrcode *tar_strategy_get_qrcode(void)
> +{
> +	if (tar_current != &qr_list_head)
> +		return tar_current->qr;
> +	return 0;
> +}
> +
> +#define MESSAGE_DEFAULT_PACKET_SIZE 300
> +
> +static DEFINE_MUTEX(message_count_mutex);
> +
> +static int message_count;
> +
> +static void make_bk1_message(void)
> +{
> +	int remaining_bytes;
> +	int packet_length;
> +	int left;
> +	int packet_count;
> +	int packet_id;
> +	int message_id;
> +
> +	mutex_lock(&message_count_mutex);
> +	++message_count;
> +	message_id = message_count;
> +	mutex_unlock(&message_count_mutex);
> +
> +	packet_count = buf_pos / MESSAGE_DEFAULT_PACKET_SIZE;
> +	if (buf_pos % MESSAGE_DEFAULT_PACKET_SIZE)
> +		++packet_count;
> +
> +	left = 0;
> +	packet_id = 0;
> +	while (left < buf_pos) {
> +		remaining_bytes = buf_pos - left;
> +		packet_length = MESSAGE_DEFAULT_PACKET_SIZE;
> +		if (packet_length > remaining_bytes)
> +			packet_length = remaining_bytes;
> +		++packet_id;
> +		make_bk1_packet(qr_buffer + left, packet_length, message_id,
> +				packet_id, packet_count);
> +		left += packet_length;
> +	}
> +}
> +
> +static void print_messages(void)
> +{
> +	struct qr_chunk *it;
> +	int last_message_id = -1;
> +
> +	pr_info("QR: ids of messages in queue: ");
> +	list_for_each_entry(it, &qr_list_head, list)
> +		if (it->message_id != last_message_id) {
> +			last_message_id = it->message_id;
> +			pr_cont("%d ", last_message_id);
> +		}
> +	pr_cont("\n");
> +}
> +
> +static void print_packets(void)
> +{
> +	struct qr_chunk *it;
> +
> +	pr_info("QR: packets in queue <message, packet>: ");
> +	list_for_each_entry(it, &qr_list_head, list)
> +		pr_cont("<%d, %d> ", it->message_id, it->packet_id);
> +	pr_cont("\n");
> +}
> +
> +static void print_packets_by_msg(int message_id)
> +{
> +	struct qr_chunk *it;
> +
> +	pr_info("QR: packets in queue for message id %d: ", message_id);
> +	list_for_each_entry(it, &qr_list_head, list)
> +		if (it->message_id == message_id)
> +			pr_cont("%d ", it->packet_id);
> +	pr_cont("\n");
> +}
> +
> +static struct fb_info *info;
> +static struct fb_fillrect rect;
> +static int qr_total_width;
> +static int qr_offset_x;
> +static int qr_offset_y;
> +
> +static void clear_last_qr(void)
> +{
> +	if (info) {
> +		pr_emerg("QR: framebuffer clear\n");
> +
> +		console_lock();
> +
> +		rect.width = qr_total_width;
> +		rect.height = qr_total_width;
> +		rect.dx = qr_offset_x;
> +		rect.dy = qr_offset_y;
> +		rect.rop = 0;
> +		rect.color = QQQ_BLACK;
> +		cfb_fillrect(info, &rect);
> +
> +		console_unlock();
> +	}
> +}
> +
> +#define QR_OOPS_CMD_NOTHING 0
> +#define QR_OOPS_CMD_PRINT_MESSAGES 1
> +#define QR_OOPS_CMD_PRINT_PACKETS 2
> +#define QR_OOPS_CMD_DELETE_MESSAGE 3
> +#define QR_OOPS_CMD_DELETE_PACKET 4
> +#define QR_OOPS_CMD_PAUSE 5
> +#define QR_OOPS_CMD_RESUME 6
> +#define QR_OOPS_CMD_CLEAR_QUEUE 7
> +#define QR_OOPS_CMD_STOP 8
> +#define QR_THREAD_TIME_STEP (750 * 1000)
> +
> +int qr_thread_func(void *data)
> +{
> +	struct qrcode *curr_qr = 0;
> +	int w;
> +	int last_time;
> +	int time_accumulator;
> +	int time_now;
> +	int elapsed_time;
> +	int paused = 0;
> +	int changed = 0;
> +	int cmd = 0;
> +	int param0 = 0;
> +	int param1 = 0;
> +
> +	qr_total_width = 0;
> +	qr_offset_x = 0;
> +	qr_offset_y = 0;
> +
> +	info = registered_fb[0];
> +	if (!info)
> +		pr_emerg("QR: Unable to get hand of a framebuffer!\n");
> +
> +	tar_strategy_init();
> +
> +	qr_oops_cmd = QR_OOPS_CMD_NOTHING;
> +	qr_oops_param0 = 0;
> +	qr_oops_param1 = 0;
> +
> +	last_time = jiffies_to_usecs(get_jiffies_64());
> +	time_accumulator = 0;
> +
> +	while (true) {
> +		msleep(100);
> +
> +		time_now = jiffies_to_usecs(get_jiffies_64());
> +		elapsed_time = time_now - last_time;
> +		last_time = time_now;
> +
> +		cmd = qr_oops_cmd;
> +		qr_oops_cmd = QR_OOPS_CMD_NOTHING;
> +		param0 = qr_oops_param0;
> +		param1 = qr_oops_param1;
> +
> +		if (cmd != QR_OOPS_CMD_NOTHING) {
> +			qr_oops_param0 = 0;
> +			qr_oops_param1 = 0;
> +		}
> +
> +		switch (cmd) {
> +		case QR_OOPS_CMD_NOTHING:
> +			break;
> +		case QR_OOPS_CMD_PRINT_MESSAGES:
> +			print_messages();
> +			break;
> +		case QR_OOPS_CMD_PRINT_PACKETS:
> +			if (param0)
> +				print_packets_by_msg(param0);
> +			else
> +				print_packets();
> +			break;
> +		case QR_OOPS_CMD_DELETE_MESSAGE:
> +			qr_list_delete_message(param0);
> +			tar_strategy_init();
> +			break;
> +		case QR_OOPS_CMD_DELETE_PACKET:
> +			qr_list_delete_packet(param0, param1);
> +			tar_strategy_init();
> +			break;
> +		case QR_OOPS_CMD_PAUSE:
> +			if (!paused) {
> +				paused = 1;
> +				clear_last_qr();
> +			}
> +			break;
> +		case QR_OOPS_CMD_RESUME:
> +			paused = 0;
> +			break;
> +		case QR_OOPS_CMD_CLEAR_QUEUE:
> +			qr_list_clear();
> +			tar_strategy_init();
> +			break;
> +		case QR_OOPS_CMD_STOP:
> +			/*
> +			 *  Not implemented
> +			 */
> +			break;
> +		default:
> +			pr_emerg("QR: invalid command: %d\n", qr_oops_cmd);
> +			break;
> +		}
> +
> +		if (paused)
> +			continue;
> +
> +		changed = 0;
> +		time_accumulator += elapsed_time;
> +		if (time_accumulator > QR_THREAD_TIME_STEP) {
> +			time_accumulator -= QR_THREAD_TIME_STEP;
> +
> +			tar_strategy_next_step();
> +			if (curr_qr != tar_strategy_get_qrcode()) {
> +				curr_qr = tar_strategy_get_qrcode();
> +				changed = 1;
> +			}
> +
> +			if (changed && info)
> +				pr_emerg("QR: force console flush\n");
> +		}
> +
> +		if (!curr_qr) {
> +			if (changed)
> +				clear_last_qr();
> +			continue;
> +		}
> +
> +		if (info) {
> +			console_lock();
> +
> +			rect.width = qr_total_width;
> +			rect.height = qr_total_width;
> +			rect.dx = qr_offset_x;
> +			rect.dy = qr_offset_y;
> +			rect.rop = 0;
> +			rect.color = QQQ_BLACK;
> +			cfb_fillrect(info, &rect);
> +
> +			w = compute_w(info, curr_qr->width);
> +			qr_total_width = (curr_qr->width + 2) * w;
> +			qr_offset_x = info->var.xres - qr_total_width;
> +			qr_offset_y = 0;
> +
> +			draw_qr(info, curr_qr, qr_offset_x, qr_offset_y,
> +				w, w, 1);
> +
> +			console_unlock();
> +		} else if (changed) {
> +			draw_ascii_qr(curr_qr);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int qr_thread_init(void)
> +{
> +	char qr_thread_name[] = "qr_message_thread";
> +
> +	qr_thread = kthread_create(qr_thread_func, NULL, qr_thread_name);
> +	if (qr_thread)
> +		wake_up_process(qr_thread);
> +
> +	return 0;
> +}
> +
> +void qr_thread_cleanup(void)
> +{
> +	int ret;
> +
> +	ret = kthread_stop(qr_thread);
> +	if (!ret)
> +		pr_emerg("QR thread stopped");
> +}
> +
> +void qr_append(char *text)
> +{
> +	size_t len;
> +
> +	len = strlen(text);
> +	if (len + buf_pos >= MESSAGE_BUFSIZE - 1) {
> +		len = MESSAGE_BUFSIZE - 1 - buf_pos;
> +		qr_buffer[MESSAGE_BUFSIZE - 1] = '\0';
> +	}
> +	memcpy(&qr_buffer[buf_pos], text, len);
> +	buf_pos += len;
> +}
> +
> +void print_qr_err(void)
> +{
> +	make_bk1_message();
> +
> +	buf_pos = 0;
> +
> +	if (!qr_thread)
> +		qr_thread_init();
> +}
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> index 8f0324e..916f3ed 100644
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -46,6 +46,7 @@
>   #include <linux/utsname.h>
>   #include <linux/ctype.h>
>   #include <linux/uio.h>
> +#include <linux/print_oops.h>
>
>   #include <asm/uaccess.h>
>
> @@ -1750,6 +1751,11 @@ asmlinkage int vprintk_emit(int facility, int level,
>   		}
>   	}
>
> +#ifdef CONFIG_QR_OOPS
> +	if (oops_in_progress)
> +		qr_append(text);
> +#endif
> +
>   	if (level == LOGLEVEL_DEFAULT)
>   		level = default_message_loglevel;
>
> @@ -1898,6 +1904,14 @@ asmlinkage __visible int printk(const char *fmt, ...)
>   	va_list args;
>   	int r;
>
> +#ifdef CONFIG_KGDB_KDB
> +	if (unlikely(kdb_trap_printk)) {
> +		va_start(args, fmt);
> +		r = vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
> +		va_end(args);
> +		return r;
> +	}
> +#endif

Not quite sure what this is doing here, can you split this out into a
separate patch?

>   	va_start(args, fmt);
>
>   	/*
> @@ -1910,7 +1924,6 @@ asmlinkage __visible int printk(const char *fmt, ...)
>   	r = vprintk_func(fmt, args);
>
>   	va_end(args);
> -
>   	return r;
>   }
>   EXPORT_SYMBOL(printk);
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 2e491ac..c782919 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -492,6 +492,12 @@ config SIGNATURE
>   	  Digital signature verification. Currently only RSA is supported.
>   	  Implementation is done using GnuPG MPI library
>
> +config QRLIB
> +	bool "QR encoding library"
> +	select ZLIB_DEFLATE
> +	help
> +	  QR encoding library
> +
>   #
>   # libfdt files, only selected if needed.
>   #
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index ab76b99..822fe7d 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -15,6 +15,23 @@ config PRINTK_TIME
>   	  The behavior is also controlled by the kernel command line
>   	  parameter printk.time=1. See Documentation/kernel-parameters.txt
>
> +config QR_OOPS
> +	bool "Display QR barcode for Oops messages for debugging purposes"
> +	depends on PRINTK && FB
> +	select QRLIB
> +	select ZLIB_DEFLATE
> +	select ZLIB_INFLATE
> +	select FB_CFB_FILLRECT
> +	select REED_SOLOMON
> +	select REED_SOLOMON_ENC8
> +	help
> +		Selecting this option makes printk() calls to accumulate
> +		the Oops messages in a buffer, compresses the message
> +		and prints the OR to the frame buffer device.
> +
> +		This is an experimental feature at the moment.
> +
> +
>   config MESSAGE_LOGLEVEL_DEFAULT
>   	int "Default message log level (1-7)"
>   	range 1 7
> diff --git a/lib/Makefile b/lib/Makefile
> index 13a7c6a..97da07e 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -50,6 +50,9 @@ endif
>   obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o
>   CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any)
>
> +# QR lib
> +obj-$(CONFIG_QRLIB) += qr/
> +
>   obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
>   obj-$(CONFIG_GENERIC_PCI_IOMAP) += pci_iomap.o
>   obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o
> diff --git a/lib/qr/Makefile b/lib/qr/Makefile
> new file mode 100644
> index 0000000..d0c0f13
> --- /dev/null
> +++ b/lib/qr/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# QR encoding library
> +#
> +
> +EXTRA_FLAGS = -g
> +obj-$(CONFIG_QRLIB) = bitstream.o mask.o qrencode.o qrinput.o qrspec.o split.o
> diff --git a/lib/qr/bitstream.c b/lib/qr/bitstream.c
> new file mode 100644
> index 0000000..a2f02e9
> --- /dev/null
> +++ b/lib/qr/bitstream.c
> @@ -0,0 +1,203 @@
> +/*
> + * bit_stream - storage of bits to which you can append
> + *
> + * Copyright (C) 2014 Levente Kurusa <levex@...ux.com>
> + *
> + * This library is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include "bitstream.h"
> +
> +#define BITS_TO_BYTES(x) (((x) % 8) ? ((x) / 8 + 1) : ((x) / 8))

This function is defined a few places elsewhere in the kernel, it
might be worth it to pull it out to a generic header file (bitops.h?)

> +
> +u8 *__bit_stream_alloc_data(int len, gfp_t gfp)
> +{
> +	return kzalloc(BITS_TO_BYTES(len), gfp);
> +}

This function doesn't help anything. Just put the kzalloc inline.

> +
> +struct bit_stream *bit_stream_allocate(int space, gfp_t gfp)
> +{
> +	struct bit_stream *bstr;
> +	u8 *bitmap;
> +
> +	/* XXX */
> +	gfp = GFP_ATOMIC;

I'm guessing this is atomic because it's not possible to determine
the gfp flags?

> +
> +	bstr = kzalloc(sizeof(*bstr), gfp);
> +	if (!bstr)
> +		return NULL;
> +
> +	bstr->gfp = gfp;
> +
> +	if (space == 0)
> +		return bstr;
> +
> +	bitmap = __bit_stream_alloc_data(space, gfp);
> +	if (!bitmap) {
> +		kfree(bstr);
> +		return NULL;
> +	}
> +
> +	bstr->data = bitmap;
> +	bstr->space = space;
> +	bstr->length = 0;
> +	return bstr;
> +}
> +
> +struct bit_stream *bit_stream_new(void)
> +{
> +	return bit_stream_allocate(128, GFP_ATOMIC);
> +}
> +
> +void bit_stream_free(struct bit_stream *bstr)
> +{
> +	if (!bstr)
> +		return;
> +	kfree(bstr->data);
> +	kfree(bstr);
> +}
> +
> +int bit_stream_resize(struct bit_stream *bstr, int nspace)
> +{
> +	unsigned char *data;
> +
> +	if (nspace == 0)
> +		return -EINVAL;
> +
> +	if (bstr->length >= nspace)
> +		return -ENOSPC;
> +
> +	data = kzalloc(BITS_TO_BYTES(nspace), bstr->gfp);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	if (bstr->data) {
> +		memcpy(data, bstr->data, BITS_TO_BYTES(bstr->length));
> +		kfree(bstr->data);
> +	}
> +
> +	bstr->data = data;
> +	bstr->space = nspace;
> +	return 0;
> +}
> +
> +int __bit_stream_get_bit(struct bit_stream *bstr, int no)
> +{
> +	if (WARN_ON(no > bstr->length))
> +		return 0;
> +
> +	return (bstr->data[no / 8] & (1 << (no % 8))) ? 1 : 0;
> +}
> +
> +int __bit_stream_append_bit(struct bit_stream *bstr, u8 bit)
> +{
> +	int rc;
> +
> +	if (!bstr->data || bstr->length + 1 >= bstr->space) {
> +		rc = bit_stream_resize(bstr, bstr->space + 256);
> +		if (rc)
> +			return rc;
> +	}
> +
> +	if (bit != 0)
> +		bstr->data[bstr->length / 8] |= (1UL << (bstr->length % 8));
> +	else
> +		bstr->data[bstr->length / 8] &= ~(1UL << (bstr->length % 8));
> +
> +	bstr->length++;
> +
> +	return 0;
> +}
> +
> +int bit_stream_append_bytes(struct bit_stream *bstr, int bytes, u8 *data)
> +{
> +	int rc;
> +	int i, j;
> +	unsigned char mask;
> +
> +	for (i = 0; i < bytes; i++) {
> +		mask = 0x80;
> +		for (j = 0; j < 8; j++) {
> +			rc = __bit_stream_append_bit(bstr, data[i] & mask);
> +			if (rc)
> +				return rc;
> +			mask = mask >> 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int bit_stream_append_num(struct bit_stream *bstr, int bits, int num)
> +{
> +	int rc;
> +	int i;
> +	unsigned int mask;
> +
> +	mask = 1 << (bits - 1);
> +	for (i = 0; i < bits; i++) {
> +		rc = __bit_stream_append_bit(bstr, num & mask);
> +		if (rc)
> +			return rc;
> +		mask = mask >> 1;
> +	}
> +
> +	return 0;
> +}
> +
> +int bit_stream_append(struct bit_stream *dst, struct bit_stream *src)
> +{
> +	int rc, i;
> +
> +	for (i = 0; i < src->length; i++) {
> +		rc = __bit_stream_append_bit(dst, __bit_stream_get_bit(src, i));
> +		if (rc)
> +			return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +unsigned char *bit_stream_to_byte(struct bit_stream *bstr)
> +{
> +	unsigned char *data, v;
> +	int i, j, size, bytes, p;
> +
> +	data = kzalloc((bstr->length + 7) / 8, bstr->gfp);
> +	if (!data)
> +		return NULL;
> +	size = bit_stream_size(bstr);
> +	bytes = size / 8;
> +	p = 0;
> +	for (i = 0; i < bytes; i++) {
> +		v = 0;
> +		for (j = 0; j < 8; j++) {
> +			v = v << 1;
> +			v |= __bit_stream_get_bit(bstr, p);
> +			p++;
> +		}
> +		data[i] = v;
> +	}
> +	if (size & 7) {
> +		v = 0;
> +		for (j = 0; j < (size & 7); j++) {
> +			v = v << 1;
> +			v |= __bit_stream_get_bit(bstr, p);
> +			p++;
> +		}
> +		data[bytes] = v;
> +	}
> +
> +	return data;
> +}

<snip>

> diff --git a/lib/qr/qrinput.c b/lib/qr/qrinput.c
> new file mode 100644
> index 0000000..a7d9779
> --- /dev/null
> +++ b/lib/qr/qrinput.c
> @@ -0,0 +1,1515 @@
> +/*
> + * qrencode - QR Code encoder
> + *
> + * Input data chunk class
> + * Copyright (C) 2014 Levente Kurusa <levex@...ux.com>
> + * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@...uchi.org>
> + *
> + * This library is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include <linux/qrencode.h>
> +#include "qrspec.h"
> +#include "bitstream.h"
> +#include "qrinput.h"
> +
> +/******************************************************************************
> + * Utilities
> + *****************************************************************************/
> +int qrinput_is_splittable_mode(enum qrencode_mode mode)
> +{
> +	return mode >= QR_MODE_NUM;
> +}
> +
> +/******************************************************************************
> + * Entry of input data
> + *****************************************************************************/
> +
> +static
> +struct qrinput_list *qrinput_list_new_entry(enum qrencode_mode mode,
> +					    int size,
> +					    const unsigned char *data)
> +{
> +	struct qrinput_list *entry;
> +
> +	if (qrinput_check(mode, size, data))
> +		return NULL;
> +
> +	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
> +	if (!entry)
> +		return NULL;
> +
> +	entry->mode = mode;
> +	entry->size = size;
> +	if (size > 0) {
> +		entry->data = kmalloc(size, GFP_ATOMIC);
> +		if (!entry->data) {
> +			kfree(entry);
> +			return NULL;
> +		}
> +		memcpy(entry->data, data, size);
> +	}
> +	entry->bstream = NULL;
> +	entry->next = NULL;
> +
> +	return entry;
> +}
> +
> +static void qrinput_list_free_entry(struct qrinput_list *entry)
> +{
> +	if (!entry) {
> +		kfree(entry->data);
> +		bit_stream_free(entry->bstream);
> +		kfree(entry);
> +	}
> +}
> +
> +static struct qrinput_list *qrinput_list_dup(struct qrinput_list *entry)
> +{
> +	struct qrinput_list *n;
> +
> +	n = kmalloc(sizeof(*n), GFP_ATOMIC);
> +	if (!n)
> +		return NULL;
> +
> +	n->mode = entry->mode;
> +	n->size = entry->size;
> +	n->data = kmalloc(n->size, GFP_ATOMIC);
> +	if (!n->data) {
> +		kfree(n);
> +		return NULL;
> +	}
> +	memcpy(n->data, entry->data, entry->size);
> +	n->bstream = NULL;
> +	n->next = NULL;
> +
> +	return n;
> +}
> +
> +/******************************************************************************
> + * Input Data
> + *****************************************************************************/
> +
> +struct qrinput *qrinput_new(void)
> +{
> +	return qrinput_new2(0, QR_ECLEVEL_L);
> +}
> +
> +struct qrinput *qrinput_new2(int version, enum qrec_level level)
> +{
> +	struct qrinput *input;
> +
> +	if (version < 0 || version > QRSPEC_VERSION_MAX || level > QR_ECLEVEL_H)
> +		return NULL;
> +
> +	input = kmalloc(sizeof(*input), GFP_ATOMIC);
> +	if (!input)
> +		return NULL;
> +
> +	input->head = NULL;
> +	input->tail = NULL;
> +	input->version = version;
> +	input->level = level;
> +	input->fnc1 = 0;
> +
> +	return input;
> +}
> +
> +int qrinput_get_version(struct qrinput *input)
> +{
> +	return input->version;
> +}
> +
> +int qrinput_set_version(struct qrinput *input, int version)
> +{
> +	if (version < 0 || version > QRSPEC_VERSION_MAX)
> +		return -1;

This and a bunch of the other functions need to return actual
error codes and not just -1

> <snip>

> diff --git a/lib/qr/split.c b/lib/qr/split.c
> new file mode 100644
> index 0000000..922f9a0
> --- /dev/null
> +++ b/lib/qr/split.c
> @@ -0,0 +1,283 @@
> +/*
> + * qrencode - QR Code encoder
> + *
> + * Input data splitter.
> + * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@...uchi.org>
> + *
> + * The following data / specifications are taken from
> + * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
> + *  or
> + * "Automatic identification and data capture techniques --
> + *  QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
> + *
> + * This library is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include <linux/qrencode.h>
> +#include "qrinput.h"
> +#include "qrspec.h"
> +#include "split.h"
> +
> +#define isdigit(__c__) ((unsigned char)((signed char)(__c__) - '0') < 10)
> +#define isalnum(__c__) (qrinput_look_an_table(__c__) >= 0)

These are already defined in ctypes.h do, do they need to be redefined?


Thanks,
Laura

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ