162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/***************************************************************************
362306a36Sopenharmony_ci *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
462306a36Sopenharmony_ci *                                                                         *
562306a36Sopenharmony_ci *   Based on Logitech G13 driver (v0.4)                                   *
662306a36Sopenharmony_ci *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
762306a36Sopenharmony_ci *                                                                         *
862306a36Sopenharmony_ci ***************************************************************************/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/hid.h>
1162306a36Sopenharmony_ci#include <linux/hid-debug.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/fb.h>
1462306a36Sopenharmony_ci#include <linux/seq_file.h>
1562306a36Sopenharmony_ci#include <linux/debugfs.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/uaccess.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "hid-picolcd.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int picolcd_debug_reset_show(struct seq_file *f, void *p)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	if (picolcd_fbinfo((struct picolcd_data *)f->private))
2662306a36Sopenharmony_ci		seq_printf(f, "all fb\n");
2762306a36Sopenharmony_ci	else
2862306a36Sopenharmony_ci		seq_printf(f, "all\n");
2962306a36Sopenharmony_ci	return 0;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int picolcd_debug_reset_open(struct inode *inode, struct file *f)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return single_open(f, picolcd_debug_reset_show, inode->i_private);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
3862306a36Sopenharmony_ci		size_t count, loff_t *ppos)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
4162306a36Sopenharmony_ci	char buf[32];
4262306a36Sopenharmony_ci	size_t cnt = min(count, sizeof(buf)-1);
4362306a36Sopenharmony_ci	if (copy_from_user(buf, user_buf, cnt))
4462306a36Sopenharmony_ci		return -EFAULT;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
4762306a36Sopenharmony_ci		cnt--;
4862306a36Sopenharmony_ci	buf[cnt] = '\0';
4962306a36Sopenharmony_ci	if (strcmp(buf, "all") == 0) {
5062306a36Sopenharmony_ci		picolcd_reset(data->hdev);
5162306a36Sopenharmony_ci		picolcd_fb_reset(data, 1);
5262306a36Sopenharmony_ci	} else if (strcmp(buf, "fb") == 0) {
5362306a36Sopenharmony_ci		picolcd_fb_reset(data, 1);
5462306a36Sopenharmony_ci	} else {
5562306a36Sopenharmony_ci		return -EINVAL;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	return count;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic const struct file_operations picolcd_debug_reset_fops = {
6162306a36Sopenharmony_ci	.owner    = THIS_MODULE,
6262306a36Sopenharmony_ci	.open     = picolcd_debug_reset_open,
6362306a36Sopenharmony_ci	.read     = seq_read,
6462306a36Sopenharmony_ci	.llseek   = seq_lseek,
6562306a36Sopenharmony_ci	.write    = picolcd_debug_reset_write,
6662306a36Sopenharmony_ci	.release  = single_release,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * The "eeprom" file
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
7362306a36Sopenharmony_ci		size_t s, loff_t *off)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct picolcd_data *data = f->private_data;
7662306a36Sopenharmony_ci	struct picolcd_pending *resp;
7762306a36Sopenharmony_ci	u8 raw_data[3];
7862306a36Sopenharmony_ci	ssize_t ret = -EIO;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (s == 0)
8162306a36Sopenharmony_ci		return -EINVAL;
8262306a36Sopenharmony_ci	if (*off > 0x0ff)
8362306a36Sopenharmony_ci		return 0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* prepare buffer with info about what we want to read (addr & len) */
8662306a36Sopenharmony_ci	raw_data[0] = *off & 0xff;
8762306a36Sopenharmony_ci	raw_data[1] = (*off >> 8) & 0xff;
8862306a36Sopenharmony_ci	raw_data[2] = s < 20 ? s : 20;
8962306a36Sopenharmony_ci	if (*off + raw_data[2] > 0xff)
9062306a36Sopenharmony_ci		raw_data[2] = 0x100 - *off;
9162306a36Sopenharmony_ci	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
9262306a36Sopenharmony_ci			sizeof(raw_data));
9362306a36Sopenharmony_ci	if (!resp)
9462306a36Sopenharmony_ci		return -EIO;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
9762306a36Sopenharmony_ci		/* successful read :) */
9862306a36Sopenharmony_ci		ret = resp->raw_data[2];
9962306a36Sopenharmony_ci		if (ret > s)
10062306a36Sopenharmony_ci			ret = s;
10162306a36Sopenharmony_ci		if (copy_to_user(u, resp->raw_data+3, ret))
10262306a36Sopenharmony_ci			ret = -EFAULT;
10362306a36Sopenharmony_ci		else
10462306a36Sopenharmony_ci			*off += ret;
10562306a36Sopenharmony_ci	} /* anything else is some kind of IO error */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	kfree(resp);
10862306a36Sopenharmony_ci	return ret;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
11262306a36Sopenharmony_ci		size_t s, loff_t *off)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct picolcd_data *data = f->private_data;
11562306a36Sopenharmony_ci	struct picolcd_pending *resp;
11662306a36Sopenharmony_ci	ssize_t ret = -EIO;
11762306a36Sopenharmony_ci	u8 raw_data[23];
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (s == 0)
12062306a36Sopenharmony_ci		return -EINVAL;
12162306a36Sopenharmony_ci	if (*off > 0x0ff)
12262306a36Sopenharmony_ci		return -ENOSPC;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	memset(raw_data, 0, sizeof(raw_data));
12562306a36Sopenharmony_ci	raw_data[0] = *off & 0xff;
12662306a36Sopenharmony_ci	raw_data[1] = (*off >> 8) & 0xff;
12762306a36Sopenharmony_ci	raw_data[2] = min_t(size_t, 20, s);
12862306a36Sopenharmony_ci	if (*off + raw_data[2] > 0xff)
12962306a36Sopenharmony_ci		raw_data[2] = 0x100 - *off;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
13262306a36Sopenharmony_ci		return -EFAULT;
13362306a36Sopenharmony_ci	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
13462306a36Sopenharmony_ci			sizeof(raw_data));
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!resp)
13762306a36Sopenharmony_ci		return -EIO;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
14062306a36Sopenharmony_ci		/* check if written data matches */
14162306a36Sopenharmony_ci		if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
14262306a36Sopenharmony_ci			*off += raw_data[2];
14362306a36Sopenharmony_ci			ret = raw_data[2];
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci	kfree(resp);
14762306a36Sopenharmony_ci	return ret;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/*
15162306a36Sopenharmony_ci * Notes:
15262306a36Sopenharmony_ci * - read/write happens in chunks of at most 20 bytes, it's up to userspace
15362306a36Sopenharmony_ci *   to loop in order to get more data.
15462306a36Sopenharmony_ci * - on write errors on otherwise correct write request the bytes
15562306a36Sopenharmony_ci *   that should have been written are in undefined state.
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic const struct file_operations picolcd_debug_eeprom_fops = {
15862306a36Sopenharmony_ci	.owner    = THIS_MODULE,
15962306a36Sopenharmony_ci	.open     = simple_open,
16062306a36Sopenharmony_ci	.read     = picolcd_debug_eeprom_read,
16162306a36Sopenharmony_ci	.write    = picolcd_debug_eeprom_write,
16262306a36Sopenharmony_ci	.llseek   = generic_file_llseek,
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/*
16662306a36Sopenharmony_ci * The "flash" file
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_ci/* record a flash address to buf (bounds check to be done by caller) */
16962306a36Sopenharmony_cistatic int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	buf[0] = off & 0xff;
17262306a36Sopenharmony_ci	buf[1] = (off >> 8) & 0xff;
17362306a36Sopenharmony_ci	if (data->addr_sz == 3)
17462306a36Sopenharmony_ci		buf[2] = (off >> 16) & 0xff;
17562306a36Sopenharmony_ci	return data->addr_sz == 2 ? 2 : 3;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* read a given size of data (bounds check to be done by caller) */
17962306a36Sopenharmony_cistatic ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
18062306a36Sopenharmony_ci		char __user *u, size_t s, loff_t *off)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct picolcd_pending *resp;
18362306a36Sopenharmony_ci	u8 raw_data[4];
18462306a36Sopenharmony_ci	ssize_t ret = 0;
18562306a36Sopenharmony_ci	int len_off, err = -EIO;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	while (s > 0) {
18862306a36Sopenharmony_ci		err = -EIO;
18962306a36Sopenharmony_ci		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
19062306a36Sopenharmony_ci		raw_data[len_off] = s > 32 ? 32 : s;
19162306a36Sopenharmony_ci		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
19262306a36Sopenharmony_ci		if (!resp || !resp->in_report)
19362306a36Sopenharmony_ci			goto skip;
19462306a36Sopenharmony_ci		if (resp->in_report->id == REPORT_MEMORY ||
19562306a36Sopenharmony_ci			resp->in_report->id == REPORT_BL_READ_MEMORY) {
19662306a36Sopenharmony_ci			if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
19762306a36Sopenharmony_ci				goto skip;
19862306a36Sopenharmony_ci			if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
19962306a36Sopenharmony_ci				err = -EFAULT;
20062306a36Sopenharmony_ci				goto skip;
20162306a36Sopenharmony_ci			}
20262306a36Sopenharmony_ci			*off += raw_data[len_off];
20362306a36Sopenharmony_ci			s    -= raw_data[len_off];
20462306a36Sopenharmony_ci			ret  += raw_data[len_off];
20562306a36Sopenharmony_ci			err   = 0;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ciskip:
20862306a36Sopenharmony_ci		kfree(resp);
20962306a36Sopenharmony_ci		if (err)
21062306a36Sopenharmony_ci			return ret > 0 ? ret : err;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
21662306a36Sopenharmony_ci		size_t s, loff_t *off)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct picolcd_data *data = f->private_data;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (s == 0)
22162306a36Sopenharmony_ci		return -EINVAL;
22262306a36Sopenharmony_ci	if (*off > 0x05fff)
22362306a36Sopenharmony_ci		return 0;
22462306a36Sopenharmony_ci	if (*off + s > 0x05fff)
22562306a36Sopenharmony_ci		s = 0x06000 - *off;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (data->status & PICOLCD_BOOTLOADER)
22862306a36Sopenharmony_ci		return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
22962306a36Sopenharmony_ci	else
23062306a36Sopenharmony_ci		return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/* erase block aligned to 64bytes boundary */
23462306a36Sopenharmony_cistatic ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
23562306a36Sopenharmony_ci		loff_t *off)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct picolcd_pending *resp;
23862306a36Sopenharmony_ci	u8 raw_data[3];
23962306a36Sopenharmony_ci	int len_off;
24062306a36Sopenharmony_ci	ssize_t ret = -EIO;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (*off & 0x3f)
24362306a36Sopenharmony_ci		return -EINVAL;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	len_off = _picolcd_flash_setaddr(data, raw_data, *off);
24662306a36Sopenharmony_ci	resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
24762306a36Sopenharmony_ci	if (!resp || !resp->in_report)
24862306a36Sopenharmony_ci		goto skip;
24962306a36Sopenharmony_ci	if (resp->in_report->id == REPORT_MEMORY ||
25062306a36Sopenharmony_ci		resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
25162306a36Sopenharmony_ci		if (memcmp(raw_data, resp->raw_data, len_off) != 0)
25262306a36Sopenharmony_ci			goto skip;
25362306a36Sopenharmony_ci		ret = 0;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ciskip:
25662306a36Sopenharmony_ci	kfree(resp);
25762306a36Sopenharmony_ci	return ret;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/* write a given size of data (bounds check to be done by caller) */
26162306a36Sopenharmony_cistatic ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
26262306a36Sopenharmony_ci		const char __user *u, size_t s, loff_t *off)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct picolcd_pending *resp;
26562306a36Sopenharmony_ci	u8 raw_data[36];
26662306a36Sopenharmony_ci	ssize_t ret = 0;
26762306a36Sopenharmony_ci	int len_off, err = -EIO;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	while (s > 0) {
27062306a36Sopenharmony_ci		err = -EIO;
27162306a36Sopenharmony_ci		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
27262306a36Sopenharmony_ci		raw_data[len_off] = s > 32 ? 32 : s;
27362306a36Sopenharmony_ci		if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
27462306a36Sopenharmony_ci			err = -EFAULT;
27562306a36Sopenharmony_ci			break;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
27862306a36Sopenharmony_ci				len_off+1+raw_data[len_off]);
27962306a36Sopenharmony_ci		if (!resp || !resp->in_report)
28062306a36Sopenharmony_ci			goto skip;
28162306a36Sopenharmony_ci		if (resp->in_report->id == REPORT_MEMORY ||
28262306a36Sopenharmony_ci			resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
28362306a36Sopenharmony_ci			if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
28462306a36Sopenharmony_ci				goto skip;
28562306a36Sopenharmony_ci			*off += raw_data[len_off];
28662306a36Sopenharmony_ci			s    -= raw_data[len_off];
28762306a36Sopenharmony_ci			ret  += raw_data[len_off];
28862306a36Sopenharmony_ci			err   = 0;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ciskip:
29162306a36Sopenharmony_ci		kfree(resp);
29262306a36Sopenharmony_ci		if (err)
29362306a36Sopenharmony_ci			break;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci	return ret > 0 ? ret : err;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
29962306a36Sopenharmony_ci		size_t s, loff_t *off)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct picolcd_data *data = f->private_data;
30262306a36Sopenharmony_ci	ssize_t err, ret = 0;
30362306a36Sopenharmony_ci	int report_erase, report_write;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (s == 0)
30662306a36Sopenharmony_ci		return -EINVAL;
30762306a36Sopenharmony_ci	if (*off > 0x5fff)
30862306a36Sopenharmony_ci		return -ENOSPC;
30962306a36Sopenharmony_ci	if (s & 0x3f)
31062306a36Sopenharmony_ci		return -EINVAL;
31162306a36Sopenharmony_ci	if (*off & 0x3f)
31262306a36Sopenharmony_ci		return -EINVAL;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (data->status & PICOLCD_BOOTLOADER) {
31562306a36Sopenharmony_ci		report_erase = REPORT_BL_ERASE_MEMORY;
31662306a36Sopenharmony_ci		report_write = REPORT_BL_WRITE_MEMORY;
31762306a36Sopenharmony_ci	} else {
31862306a36Sopenharmony_ci		report_erase = REPORT_ERASE_MEMORY;
31962306a36Sopenharmony_ci		report_write = REPORT_WRITE_MEMORY;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	mutex_lock(&data->mutex_flash);
32262306a36Sopenharmony_ci	while (s > 0) {
32362306a36Sopenharmony_ci		err = _picolcd_flash_erase64(data, report_erase, off);
32462306a36Sopenharmony_ci		if (err)
32562306a36Sopenharmony_ci			break;
32662306a36Sopenharmony_ci		err = _picolcd_flash_write(data, report_write, u, 64, off);
32762306a36Sopenharmony_ci		if (err < 0)
32862306a36Sopenharmony_ci			break;
32962306a36Sopenharmony_ci		ret += err;
33062306a36Sopenharmony_ci		*off += err;
33162306a36Sopenharmony_ci		s -= err;
33262306a36Sopenharmony_ci		if (err != 64)
33362306a36Sopenharmony_ci			break;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	mutex_unlock(&data->mutex_flash);
33662306a36Sopenharmony_ci	return ret > 0 ? ret : err;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/*
34062306a36Sopenharmony_ci * Notes:
34162306a36Sopenharmony_ci * - concurrent writing is prevented by mutex and all writes must be
34262306a36Sopenharmony_ci *   n*64 bytes and 64-byte aligned, each write being preceded by an
34362306a36Sopenharmony_ci *   ERASE which erases a 64byte block.
34462306a36Sopenharmony_ci *   If less than requested was written or an error is returned for an
34562306a36Sopenharmony_ci *   otherwise correct write request the next 64-byte block which should
34662306a36Sopenharmony_ci *   have been written is in undefined state (mostly: original, erased,
34762306a36Sopenharmony_ci *   (half-)written with write error)
34862306a36Sopenharmony_ci * - reading can happen without special restriction
34962306a36Sopenharmony_ci */
35062306a36Sopenharmony_cistatic const struct file_operations picolcd_debug_flash_fops = {
35162306a36Sopenharmony_ci	.owner    = THIS_MODULE,
35262306a36Sopenharmony_ci	.open     = simple_open,
35362306a36Sopenharmony_ci	.read     = picolcd_debug_flash_read,
35462306a36Sopenharmony_ci	.write    = picolcd_debug_flash_write,
35562306a36Sopenharmony_ci	.llseek   = generic_file_llseek,
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/*
36062306a36Sopenharmony_ci * Helper code for HID report level dumping/debugging
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_cistatic const char * const error_codes[] = {
36362306a36Sopenharmony_ci	"success", "parameter missing", "data_missing", "block readonly",
36462306a36Sopenharmony_ci	"block not erasable", "block too big", "section overflow",
36562306a36Sopenharmony_ci	"invalid command length", "invalid data length",
36662306a36Sopenharmony_ci};
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
36962306a36Sopenharmony_ci		const size_t data_len)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int i, j;
37262306a36Sopenharmony_ci	for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
37362306a36Sopenharmony_ci		dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
37462306a36Sopenharmony_ci		dst[j++] = hex_asc[data[i] & 0x0f];
37562306a36Sopenharmony_ci		dst[j++] = ' ';
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	dst[j]   = '\0';
37862306a36Sopenharmony_ci	if (j > 0)
37962306a36Sopenharmony_ci		dst[j-1] = '\n';
38062306a36Sopenharmony_ci	if (i < data_len && j > 2)
38162306a36Sopenharmony_ci		dst[j-2] = dst[j-3] = '.';
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_civoid picolcd_debug_out_report(struct picolcd_data *data,
38562306a36Sopenharmony_ci		struct hid_device *hdev, struct hid_report *report)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	u8 *raw_data;
38862306a36Sopenharmony_ci	int raw_size = (report->size >> 3) + 1;
38962306a36Sopenharmony_ci	char *buff;
39062306a36Sopenharmony_ci#define BUFF_SZ 256
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Avoid unnecessary overhead if debugfs is disabled */
39362306a36Sopenharmony_ci	if (list_empty(&hdev->debug_list))
39462306a36Sopenharmony_ci		return;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
39762306a36Sopenharmony_ci	if (!buff)
39862306a36Sopenharmony_ci		return;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	raw_data = hid_alloc_report_buf(report, GFP_ATOMIC);
40162306a36Sopenharmony_ci	if (!raw_data) {
40262306a36Sopenharmony_ci		kfree(buff);
40362306a36Sopenharmony_ci		return;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	snprintf(buff, BUFF_SZ, "\nout report %d (size %d) =  ",
40762306a36Sopenharmony_ci			report->id, raw_size);
40862306a36Sopenharmony_ci	hid_debug_event(hdev, buff);
40962306a36Sopenharmony_ci	raw_data[0] = report->id;
41062306a36Sopenharmony_ci	hid_output_report(report, raw_data);
41162306a36Sopenharmony_ci	dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
41262306a36Sopenharmony_ci	hid_debug_event(hdev, buff);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	switch (report->id) {
41562306a36Sopenharmony_ci	case REPORT_LED_STATE:
41662306a36Sopenharmony_ci		/* 1 data byte with GPO state */
41762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
41862306a36Sopenharmony_ci			"REPORT_LED_STATE", report->id, raw_size-1);
41962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
42062306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
42162306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci	case REPORT_BRIGHTNESS:
42462306a36Sopenharmony_ci		/* 1 data byte with brightness */
42562306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
42662306a36Sopenharmony_ci			"REPORT_BRIGHTNESS", report->id, raw_size-1);
42762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
42862306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
42962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
43062306a36Sopenharmony_ci		break;
43162306a36Sopenharmony_ci	case REPORT_CONTRAST:
43262306a36Sopenharmony_ci		/* 1 data byte with contrast */
43362306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
43462306a36Sopenharmony_ci			"REPORT_CONTRAST", report->id, raw_size-1);
43562306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
43662306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
43762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	case REPORT_RESET:
44062306a36Sopenharmony_ci		/* 2 data bytes with reset duration in ms */
44162306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
44262306a36Sopenharmony_ci			"REPORT_RESET", report->id, raw_size-1);
44362306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
44462306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
44562306a36Sopenharmony_ci				raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
44662306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	case REPORT_LCD_CMD:
44962306a36Sopenharmony_ci		/* 63 data bytes with LCD commands */
45062306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
45162306a36Sopenharmony_ci			"REPORT_LCD_CMD", report->id, raw_size-1);
45262306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
45362306a36Sopenharmony_ci		/* TODO: format decoding */
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case REPORT_LCD_DATA:
45662306a36Sopenharmony_ci		/* 63 data bytes with LCD data */
45762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
45862306a36Sopenharmony_ci			"REPORT_LCD_CMD", report->id, raw_size-1);
45962306a36Sopenharmony_ci		/* TODO: format decoding */
46062306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	case REPORT_LCD_CMD_DATA:
46362306a36Sopenharmony_ci		/* 63 data bytes with LCD commands and data */
46462306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
46562306a36Sopenharmony_ci			"REPORT_LCD_CMD", report->id, raw_size-1);
46662306a36Sopenharmony_ci		/* TODO: format decoding */
46762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	case REPORT_EE_READ:
47062306a36Sopenharmony_ci		/* 3 data bytes with read area description */
47162306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
47262306a36Sopenharmony_ci			"REPORT_EE_READ", report->id, raw_size-1);
47362306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
47462306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
47562306a36Sopenharmony_ci				raw_data[2], raw_data[1]);
47662306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
47762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
47862306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci	case REPORT_EE_WRITE:
48162306a36Sopenharmony_ci		/* 3+1..20 data bytes with write area description */
48262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
48362306a36Sopenharmony_ci			"REPORT_EE_WRITE", report->id, raw_size-1);
48462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
48562306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
48662306a36Sopenharmony_ci				raw_data[2], raw_data[1]);
48762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
48862306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
48962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
49062306a36Sopenharmony_ci		if (raw_data[3] == 0) {
49162306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNo data\n");
49262306a36Sopenharmony_ci		} else if (raw_data[3] + 4 <= raw_size) {
49362306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData: ");
49462306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
49562306a36Sopenharmony_ci			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
49662306a36Sopenharmony_ci		} else {
49762306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
50062306a36Sopenharmony_ci		break;
50162306a36Sopenharmony_ci	case REPORT_ERASE_MEMORY:
50262306a36Sopenharmony_ci	case REPORT_BL_ERASE_MEMORY:
50362306a36Sopenharmony_ci		/* 3 data bytes with pointer inside erase block */
50462306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
50562306a36Sopenharmony_ci			"REPORT_ERASE_MEMORY", report->id, raw_size-1);
50662306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
50762306a36Sopenharmony_ci		switch (data->addr_sz) {
50862306a36Sopenharmony_ci		case 2:
50962306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
51062306a36Sopenharmony_ci					raw_data[2], raw_data[1]);
51162306a36Sopenharmony_ci			break;
51262306a36Sopenharmony_ci		case 3:
51362306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
51462306a36Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
51562306a36Sopenharmony_ci			break;
51662306a36Sopenharmony_ci		default:
51762306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case REPORT_READ_MEMORY:
52262306a36Sopenharmony_ci	case REPORT_BL_READ_MEMORY:
52362306a36Sopenharmony_ci		/* 4 data bytes with read area description */
52462306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
52562306a36Sopenharmony_ci			"REPORT_READ_MEMORY", report->id, raw_size-1);
52662306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
52762306a36Sopenharmony_ci		switch (data->addr_sz) {
52862306a36Sopenharmony_ci		case 2:
52962306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
53062306a36Sopenharmony_ci					raw_data[2], raw_data[1]);
53162306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
53262306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
53362306a36Sopenharmony_ci			break;
53462306a36Sopenharmony_ci		case 3:
53562306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
53662306a36Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
53762306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
53862306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
53962306a36Sopenharmony_ci			break;
54062306a36Sopenharmony_ci		default:
54162306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
54462306a36Sopenharmony_ci		break;
54562306a36Sopenharmony_ci	case REPORT_WRITE_MEMORY:
54662306a36Sopenharmony_ci	case REPORT_BL_WRITE_MEMORY:
54762306a36Sopenharmony_ci		/* 4+1..32 data bytes with write adrea description */
54862306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
54962306a36Sopenharmony_ci			"REPORT_WRITE_MEMORY", report->id, raw_size-1);
55062306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
55162306a36Sopenharmony_ci		switch (data->addr_sz) {
55262306a36Sopenharmony_ci		case 2:
55362306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
55462306a36Sopenharmony_ci					raw_data[2], raw_data[1]);
55562306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
55662306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
55762306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
55862306a36Sopenharmony_ci			if (raw_data[3] == 0) {
55962306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
56062306a36Sopenharmony_ci			} else if (raw_data[3] + 4 <= raw_size) {
56162306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
56262306a36Sopenharmony_ci				hid_debug_event(hdev, buff);
56362306a36Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
56462306a36Sopenharmony_ci			} else {
56562306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
56662306a36Sopenharmony_ci			}
56762306a36Sopenharmony_ci			break;
56862306a36Sopenharmony_ci		case 3:
56962306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
57062306a36Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
57162306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
57262306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
57362306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
57462306a36Sopenharmony_ci			if (raw_data[4] == 0) {
57562306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
57662306a36Sopenharmony_ci			} else if (raw_data[4] + 5 <= raw_size) {
57762306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
57862306a36Sopenharmony_ci				hid_debug_event(hdev, buff);
57962306a36Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
58062306a36Sopenharmony_ci			} else {
58162306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
58262306a36Sopenharmony_ci			}
58362306a36Sopenharmony_ci			break;
58462306a36Sopenharmony_ci		default:
58562306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci	case REPORT_SPLASH_RESTART:
59062306a36Sopenharmony_ci		/* TODO */
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci	case REPORT_EXIT_KEYBOARD:
59362306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
59462306a36Sopenharmony_ci			"REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
59562306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
59662306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
59762306a36Sopenharmony_ci				raw_data[1] | (raw_data[2] << 8),
59862306a36Sopenharmony_ci				raw_data[2], raw_data[1]);
59962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	case REPORT_VERSION:
60262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
60362306a36Sopenharmony_ci			"REPORT_VERSION", report->id, raw_size-1);
60462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	case REPORT_DEVID:
60762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
60862306a36Sopenharmony_ci			"REPORT_DEVID", report->id, raw_size-1);
60962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci	case REPORT_SPLASH_SIZE:
61262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
61362306a36Sopenharmony_ci			"REPORT_SPLASH_SIZE", report->id, raw_size-1);
61462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	case REPORT_HOOK_VERSION:
61762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
61862306a36Sopenharmony_ci			"REPORT_HOOK_VERSION", report->id, raw_size-1);
61962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
62062306a36Sopenharmony_ci		break;
62162306a36Sopenharmony_ci	case REPORT_EXIT_FLASHER:
62262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
62362306a36Sopenharmony_ci			"REPORT_VERSION", report->id, raw_size-1);
62462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
62562306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
62662306a36Sopenharmony_ci				raw_data[1] | (raw_data[2] << 8),
62762306a36Sopenharmony_ci				raw_data[2], raw_data[1]);
62862306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
62962306a36Sopenharmony_ci		break;
63062306a36Sopenharmony_ci	default:
63162306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
63262306a36Sopenharmony_ci			"<unknown>", report->id, raw_size-1);
63362306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
63462306a36Sopenharmony_ci		break;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci	wake_up_interruptible(&hdev->debug_wait);
63762306a36Sopenharmony_ci	kfree(raw_data);
63862306a36Sopenharmony_ci	kfree(buff);
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_civoid picolcd_debug_raw_event(struct picolcd_data *data,
64262306a36Sopenharmony_ci		struct hid_device *hdev, struct hid_report *report,
64362306a36Sopenharmony_ci		u8 *raw_data, int size)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	char *buff;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci#define BUFF_SZ 256
64862306a36Sopenharmony_ci	/* Avoid unnecessary overhead if debugfs is disabled */
64962306a36Sopenharmony_ci	if (list_empty(&hdev->debug_list))
65062306a36Sopenharmony_ci		return;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
65362306a36Sopenharmony_ci	if (!buff)
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	switch (report->id) {
65762306a36Sopenharmony_ci	case REPORT_ERROR_CODE:
65862306a36Sopenharmony_ci		/* 2 data bytes with affected report and error code */
65962306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
66062306a36Sopenharmony_ci			"REPORT_ERROR_CODE", report->id, size-1);
66162306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
66262306a36Sopenharmony_ci		if (raw_data[2] < ARRAY_SIZE(error_codes))
66362306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
66462306a36Sopenharmony_ci					raw_data[2], error_codes[raw_data[2]], raw_data[1]);
66562306a36Sopenharmony_ci		else
66662306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
66762306a36Sopenharmony_ci					raw_data[2], raw_data[1]);
66862306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
66962306a36Sopenharmony_ci		break;
67062306a36Sopenharmony_ci	case REPORT_KEY_STATE:
67162306a36Sopenharmony_ci		/* 2 data bytes with key state */
67262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
67362306a36Sopenharmony_ci			"REPORT_KEY_STATE", report->id, size-1);
67462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
67562306a36Sopenharmony_ci		if (raw_data[1] == 0)
67662306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
67762306a36Sopenharmony_ci		else if (raw_data[2] == 0)
67862306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
67962306a36Sopenharmony_ci					raw_data[1], raw_data[1]);
68062306a36Sopenharmony_ci		else
68162306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
68262306a36Sopenharmony_ci					raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
68362306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci	case REPORT_IR_DATA:
68662306a36Sopenharmony_ci		/* Up to 20 byes of IR scancode data */
68762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
68862306a36Sopenharmony_ci			"REPORT_IR_DATA", report->id, size-1);
68962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
69062306a36Sopenharmony_ci		if (raw_data[1] == 0) {
69162306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
69262306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
69362306a36Sopenharmony_ci		} else if (raw_data[1] + 1 <= size) {
69462306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
69562306a36Sopenharmony_ci					raw_data[1]);
69662306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
69762306a36Sopenharmony_ci			dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
69862306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
69962306a36Sopenharmony_ci		} else {
70062306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
70162306a36Sopenharmony_ci					raw_data[1]-1);
70262306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
70362306a36Sopenharmony_ci		}
70462306a36Sopenharmony_ci		break;
70562306a36Sopenharmony_ci	case REPORT_EE_DATA:
70662306a36Sopenharmony_ci		/* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
70762306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
70862306a36Sopenharmony_ci			"REPORT_EE_DATA", report->id, size-1);
70962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
71062306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
71162306a36Sopenharmony_ci				raw_data[2], raw_data[1]);
71262306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
71362306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
71462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
71562306a36Sopenharmony_ci		if (raw_data[3] == 0) {
71662306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNo data\n");
71762306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
71862306a36Sopenharmony_ci		} else if (raw_data[3] + 4 <= size) {
71962306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData: ");
72062306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
72162306a36Sopenharmony_ci			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
72262306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
72362306a36Sopenharmony_ci		} else {
72462306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
72562306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci		break;
72862306a36Sopenharmony_ci	case REPORT_MEMORY:
72962306a36Sopenharmony_ci		/* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRITE_MEMORY */
73062306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
73162306a36Sopenharmony_ci			"REPORT_MEMORY", report->id, size-1);
73262306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
73362306a36Sopenharmony_ci		switch (data->addr_sz) {
73462306a36Sopenharmony_ci		case 2:
73562306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
73662306a36Sopenharmony_ci					raw_data[2], raw_data[1]);
73762306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
73862306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
73962306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
74062306a36Sopenharmony_ci			if (raw_data[3] == 0) {
74162306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
74262306a36Sopenharmony_ci			} else if (raw_data[3] + 4 <= size) {
74362306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
74462306a36Sopenharmony_ci				hid_debug_event(hdev, buff);
74562306a36Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
74662306a36Sopenharmony_ci			} else {
74762306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
74862306a36Sopenharmony_ci			}
74962306a36Sopenharmony_ci			break;
75062306a36Sopenharmony_ci		case 3:
75162306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
75262306a36Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
75362306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
75462306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
75562306a36Sopenharmony_ci			hid_debug_event(hdev, buff);
75662306a36Sopenharmony_ci			if (raw_data[4] == 0) {
75762306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
75862306a36Sopenharmony_ci			} else if (raw_data[4] + 5 <= size) {
75962306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
76062306a36Sopenharmony_ci				hid_debug_event(hdev, buff);
76162306a36Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
76262306a36Sopenharmony_ci			} else {
76362306a36Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
76462306a36Sopenharmony_ci			}
76562306a36Sopenharmony_ci			break;
76662306a36Sopenharmony_ci		default:
76762306a36Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
77062306a36Sopenharmony_ci		break;
77162306a36Sopenharmony_ci	case REPORT_VERSION:
77262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
77362306a36Sopenharmony_ci			"REPORT_VERSION", report->id, size-1);
77462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
77562306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
77662306a36Sopenharmony_ci				raw_data[2], raw_data[1]);
77762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
77862306a36Sopenharmony_ci		break;
77962306a36Sopenharmony_ci	case REPORT_BL_ERASE_MEMORY:
78062306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
78162306a36Sopenharmony_ci			"REPORT_BL_ERASE_MEMORY", report->id, size-1);
78262306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
78362306a36Sopenharmony_ci		/* TODO */
78462306a36Sopenharmony_ci		break;
78562306a36Sopenharmony_ci	case REPORT_BL_READ_MEMORY:
78662306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
78762306a36Sopenharmony_ci			"REPORT_BL_READ_MEMORY", report->id, size-1);
78862306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
78962306a36Sopenharmony_ci		/* TODO */
79062306a36Sopenharmony_ci		break;
79162306a36Sopenharmony_ci	case REPORT_BL_WRITE_MEMORY:
79262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
79362306a36Sopenharmony_ci			"REPORT_BL_WRITE_MEMORY", report->id, size-1);
79462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
79562306a36Sopenharmony_ci		/* TODO */
79662306a36Sopenharmony_ci		break;
79762306a36Sopenharmony_ci	case REPORT_DEVID:
79862306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
79962306a36Sopenharmony_ci			"REPORT_DEVID", report->id, size-1);
80062306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
80162306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
80262306a36Sopenharmony_ci				raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
80362306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
80462306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
80562306a36Sopenharmony_ci				raw_data[5]);
80662306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
80762306a36Sopenharmony_ci		break;
80862306a36Sopenharmony_ci	case REPORT_SPLASH_SIZE:
80962306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
81062306a36Sopenharmony_ci			"REPORT_SPLASH_SIZE", report->id, size-1);
81162306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
81262306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
81362306a36Sopenharmony_ci				(raw_data[2] << 8) | raw_data[1]);
81462306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
81562306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
81662306a36Sopenharmony_ci				(raw_data[4] << 8) | raw_data[3]);
81762306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
81862306a36Sopenharmony_ci		break;
81962306a36Sopenharmony_ci	case REPORT_HOOK_VERSION:
82062306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
82162306a36Sopenharmony_ci			"REPORT_HOOK_VERSION", report->id, size-1);
82262306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
82362306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
82462306a36Sopenharmony_ci				raw_data[1], raw_data[2]);
82562306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
82662306a36Sopenharmony_ci		break;
82762306a36Sopenharmony_ci	default:
82862306a36Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
82962306a36Sopenharmony_ci			"<unknown>", report->id, size-1);
83062306a36Sopenharmony_ci		hid_debug_event(hdev, buff);
83162306a36Sopenharmony_ci		break;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	wake_up_interruptible(&hdev->debug_wait);
83462306a36Sopenharmony_ci	kfree(buff);
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_civoid picolcd_init_devfs(struct picolcd_data *data,
83862306a36Sopenharmony_ci		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
83962306a36Sopenharmony_ci		struct hid_report *flash_r, struct hid_report *flash_w,
84062306a36Sopenharmony_ci		struct hid_report *reset)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct hid_device *hdev = data->hdev;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	mutex_init(&data->mutex_flash);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* reset */
84762306a36Sopenharmony_ci	if (reset)
84862306a36Sopenharmony_ci		data->debug_reset = debugfs_create_file("reset", 0600,
84962306a36Sopenharmony_ci				hdev->debug_dir, data, &picolcd_debug_reset_fops);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* eeprom */
85262306a36Sopenharmony_ci	if (eeprom_r || eeprom_w)
85362306a36Sopenharmony_ci		data->debug_eeprom = debugfs_create_file("eeprom",
85462306a36Sopenharmony_ci			(eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
85562306a36Sopenharmony_ci			hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* flash */
85862306a36Sopenharmony_ci	if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
85962306a36Sopenharmony_ci		data->addr_sz = flash_r->field[0]->report_count - 1;
86062306a36Sopenharmony_ci	else
86162306a36Sopenharmony_ci		data->addr_sz = -1;
86262306a36Sopenharmony_ci	if (data->addr_sz == 2 || data->addr_sz == 3) {
86362306a36Sopenharmony_ci		data->debug_flash = debugfs_create_file("flash",
86462306a36Sopenharmony_ci			(flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
86562306a36Sopenharmony_ci			hdev->debug_dir, data, &picolcd_debug_flash_fops);
86662306a36Sopenharmony_ci	} else if (flash_r || flash_w)
86762306a36Sopenharmony_ci		hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_civoid picolcd_exit_devfs(struct picolcd_data *data)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct dentry *dent;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	dent = data->debug_reset;
87562306a36Sopenharmony_ci	data->debug_reset = NULL;
87662306a36Sopenharmony_ci	debugfs_remove(dent);
87762306a36Sopenharmony_ci	dent = data->debug_eeprom;
87862306a36Sopenharmony_ci	data->debug_eeprom = NULL;
87962306a36Sopenharmony_ci	debugfs_remove(dent);
88062306a36Sopenharmony_ci	dent = data->debug_flash;
88162306a36Sopenharmony_ci	data->debug_flash = NULL;
88262306a36Sopenharmony_ci	debugfs_remove(dent);
88362306a36Sopenharmony_ci	mutex_destroy(&data->mutex_flash);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
886