18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/***************************************************************************
38c2ecf20Sopenharmony_ci *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
48c2ecf20Sopenharmony_ci *                                                                         *
58c2ecf20Sopenharmony_ci *   Based on Logitech G13 driver (v0.4)                                   *
68c2ecf20Sopenharmony_ci *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
78c2ecf20Sopenharmony_ci *                                                                         *
88c2ecf20Sopenharmony_ci ***************************************************************************/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/hid.h>
118c2ecf20Sopenharmony_ci#include <linux/hid-debug.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/fb.h>
148c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
158c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "hid-picolcd.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int picolcd_debug_reset_show(struct seq_file *f, void *p)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	if (picolcd_fbinfo((struct picolcd_data *)f->private))
268c2ecf20Sopenharmony_ci		seq_printf(f, "all fb\n");
278c2ecf20Sopenharmony_ci	else
288c2ecf20Sopenharmony_ci		seq_printf(f, "all\n");
298c2ecf20Sopenharmony_ci	return 0;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int picolcd_debug_reset_open(struct inode *inode, struct file *f)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return single_open(f, picolcd_debug_reset_show, inode->i_private);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
388c2ecf20Sopenharmony_ci		size_t count, loff_t *ppos)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
418c2ecf20Sopenharmony_ci	char buf[32];
428c2ecf20Sopenharmony_ci	size_t cnt = min(count, sizeof(buf)-1);
438c2ecf20Sopenharmony_ci	if (copy_from_user(buf, user_buf, cnt))
448c2ecf20Sopenharmony_ci		return -EFAULT;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
478c2ecf20Sopenharmony_ci		cnt--;
488c2ecf20Sopenharmony_ci	buf[cnt] = '\0';
498c2ecf20Sopenharmony_ci	if (strcmp(buf, "all") == 0) {
508c2ecf20Sopenharmony_ci		picolcd_reset(data->hdev);
518c2ecf20Sopenharmony_ci		picolcd_fb_reset(data, 1);
528c2ecf20Sopenharmony_ci	} else if (strcmp(buf, "fb") == 0) {
538c2ecf20Sopenharmony_ci		picolcd_fb_reset(data, 1);
548c2ecf20Sopenharmony_ci	} else {
558c2ecf20Sopenharmony_ci		return -EINVAL;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	return count;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const struct file_operations picolcd_debug_reset_fops = {
618c2ecf20Sopenharmony_ci	.owner    = THIS_MODULE,
628c2ecf20Sopenharmony_ci	.open     = picolcd_debug_reset_open,
638c2ecf20Sopenharmony_ci	.read     = seq_read,
648c2ecf20Sopenharmony_ci	.llseek   = seq_lseek,
658c2ecf20Sopenharmony_ci	.write    = picolcd_debug_reset_write,
668c2ecf20Sopenharmony_ci	.release  = single_release,
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * The "eeprom" file
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_cistatic ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
738c2ecf20Sopenharmony_ci		size_t s, loff_t *off)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct picolcd_data *data = f->private_data;
768c2ecf20Sopenharmony_ci	struct picolcd_pending *resp;
778c2ecf20Sopenharmony_ci	u8 raw_data[3];
788c2ecf20Sopenharmony_ci	ssize_t ret = -EIO;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (s == 0)
818c2ecf20Sopenharmony_ci		return -EINVAL;
828c2ecf20Sopenharmony_ci	if (*off > 0x0ff)
838c2ecf20Sopenharmony_ci		return 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/* prepare buffer with info about what we want to read (addr & len) */
868c2ecf20Sopenharmony_ci	raw_data[0] = *off & 0xff;
878c2ecf20Sopenharmony_ci	raw_data[1] = (*off >> 8) & 0xff;
888c2ecf20Sopenharmony_ci	raw_data[2] = s < 20 ? s : 20;
898c2ecf20Sopenharmony_ci	if (*off + raw_data[2] > 0xff)
908c2ecf20Sopenharmony_ci		raw_data[2] = 0x100 - *off;
918c2ecf20Sopenharmony_ci	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
928c2ecf20Sopenharmony_ci			sizeof(raw_data));
938c2ecf20Sopenharmony_ci	if (!resp)
948c2ecf20Sopenharmony_ci		return -EIO;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
978c2ecf20Sopenharmony_ci		/* successful read :) */
988c2ecf20Sopenharmony_ci		ret = resp->raw_data[2];
998c2ecf20Sopenharmony_ci		if (ret > s)
1008c2ecf20Sopenharmony_ci			ret = s;
1018c2ecf20Sopenharmony_ci		if (copy_to_user(u, resp->raw_data+3, ret))
1028c2ecf20Sopenharmony_ci			ret = -EFAULT;
1038c2ecf20Sopenharmony_ci		else
1048c2ecf20Sopenharmony_ci			*off += ret;
1058c2ecf20Sopenharmony_ci	} /* anything else is some kind of IO error */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	kfree(resp);
1088c2ecf20Sopenharmony_ci	return ret;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
1128c2ecf20Sopenharmony_ci		size_t s, loff_t *off)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct picolcd_data *data = f->private_data;
1158c2ecf20Sopenharmony_ci	struct picolcd_pending *resp;
1168c2ecf20Sopenharmony_ci	ssize_t ret = -EIO;
1178c2ecf20Sopenharmony_ci	u8 raw_data[23];
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (s == 0)
1208c2ecf20Sopenharmony_ci		return -EINVAL;
1218c2ecf20Sopenharmony_ci	if (*off > 0x0ff)
1228c2ecf20Sopenharmony_ci		return -ENOSPC;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	memset(raw_data, 0, sizeof(raw_data));
1258c2ecf20Sopenharmony_ci	raw_data[0] = *off & 0xff;
1268c2ecf20Sopenharmony_ci	raw_data[1] = (*off >> 8) & 0xff;
1278c2ecf20Sopenharmony_ci	raw_data[2] = min_t(size_t, 20, s);
1288c2ecf20Sopenharmony_ci	if (*off + raw_data[2] > 0xff)
1298c2ecf20Sopenharmony_ci		raw_data[2] = 0x100 - *off;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
1328c2ecf20Sopenharmony_ci		return -EFAULT;
1338c2ecf20Sopenharmony_ci	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
1348c2ecf20Sopenharmony_ci			sizeof(raw_data));
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (!resp)
1378c2ecf20Sopenharmony_ci		return -EIO;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
1408c2ecf20Sopenharmony_ci		/* check if written data matches */
1418c2ecf20Sopenharmony_ci		if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
1428c2ecf20Sopenharmony_ci			*off += raw_data[2];
1438c2ecf20Sopenharmony_ci			ret = raw_data[2];
1448c2ecf20Sopenharmony_ci		}
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	kfree(resp);
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * Notes:
1528c2ecf20Sopenharmony_ci * - read/write happens in chunks of at most 20 bytes, it's up to userspace
1538c2ecf20Sopenharmony_ci *   to loop in order to get more data.
1548c2ecf20Sopenharmony_ci * - on write errors on otherwise correct write request the bytes
1558c2ecf20Sopenharmony_ci *   that should have been written are in undefined state.
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_cistatic const struct file_operations picolcd_debug_eeprom_fops = {
1588c2ecf20Sopenharmony_ci	.owner    = THIS_MODULE,
1598c2ecf20Sopenharmony_ci	.open     = simple_open,
1608c2ecf20Sopenharmony_ci	.read     = picolcd_debug_eeprom_read,
1618c2ecf20Sopenharmony_ci	.write    = picolcd_debug_eeprom_write,
1628c2ecf20Sopenharmony_ci	.llseek   = generic_file_llseek,
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * The "flash" file
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_ci/* record a flash address to buf (bounds check to be done by caller) */
1698c2ecf20Sopenharmony_cistatic int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	buf[0] = off & 0xff;
1728c2ecf20Sopenharmony_ci	buf[1] = (off >> 8) & 0xff;
1738c2ecf20Sopenharmony_ci	if (data->addr_sz == 3)
1748c2ecf20Sopenharmony_ci		buf[2] = (off >> 16) & 0xff;
1758c2ecf20Sopenharmony_ci	return data->addr_sz == 2 ? 2 : 3;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* read a given size of data (bounds check to be done by caller) */
1798c2ecf20Sopenharmony_cistatic ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
1808c2ecf20Sopenharmony_ci		char __user *u, size_t s, loff_t *off)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct picolcd_pending *resp;
1838c2ecf20Sopenharmony_ci	u8 raw_data[4];
1848c2ecf20Sopenharmony_ci	ssize_t ret = 0;
1858c2ecf20Sopenharmony_ci	int len_off, err = -EIO;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	while (s > 0) {
1888c2ecf20Sopenharmony_ci		err = -EIO;
1898c2ecf20Sopenharmony_ci		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
1908c2ecf20Sopenharmony_ci		raw_data[len_off] = s > 32 ? 32 : s;
1918c2ecf20Sopenharmony_ci		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
1928c2ecf20Sopenharmony_ci		if (!resp || !resp->in_report)
1938c2ecf20Sopenharmony_ci			goto skip;
1948c2ecf20Sopenharmony_ci		if (resp->in_report->id == REPORT_MEMORY ||
1958c2ecf20Sopenharmony_ci			resp->in_report->id == REPORT_BL_READ_MEMORY) {
1968c2ecf20Sopenharmony_ci			if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
1978c2ecf20Sopenharmony_ci				goto skip;
1988c2ecf20Sopenharmony_ci			if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
1998c2ecf20Sopenharmony_ci				err = -EFAULT;
2008c2ecf20Sopenharmony_ci				goto skip;
2018c2ecf20Sopenharmony_ci			}
2028c2ecf20Sopenharmony_ci			*off += raw_data[len_off];
2038c2ecf20Sopenharmony_ci			s    -= raw_data[len_off];
2048c2ecf20Sopenharmony_ci			ret  += raw_data[len_off];
2058c2ecf20Sopenharmony_ci			err   = 0;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ciskip:
2088c2ecf20Sopenharmony_ci		kfree(resp);
2098c2ecf20Sopenharmony_ci		if (err)
2108c2ecf20Sopenharmony_ci			return ret > 0 ? ret : err;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	return ret;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
2168c2ecf20Sopenharmony_ci		size_t s, loff_t *off)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct picolcd_data *data = f->private_data;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (s == 0)
2218c2ecf20Sopenharmony_ci		return -EINVAL;
2228c2ecf20Sopenharmony_ci	if (*off > 0x05fff)
2238c2ecf20Sopenharmony_ci		return 0;
2248c2ecf20Sopenharmony_ci	if (*off + s > 0x05fff)
2258c2ecf20Sopenharmony_ci		s = 0x06000 - *off;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_BOOTLOADER)
2288c2ecf20Sopenharmony_ci		return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
2298c2ecf20Sopenharmony_ci	else
2308c2ecf20Sopenharmony_ci		return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci/* erase block aligned to 64bytes boundary */
2348c2ecf20Sopenharmony_cistatic ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
2358c2ecf20Sopenharmony_ci		loff_t *off)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct picolcd_pending *resp;
2388c2ecf20Sopenharmony_ci	u8 raw_data[3];
2398c2ecf20Sopenharmony_ci	int len_off;
2408c2ecf20Sopenharmony_ci	ssize_t ret = -EIO;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (*off & 0x3f)
2438c2ecf20Sopenharmony_ci		return -EINVAL;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	len_off = _picolcd_flash_setaddr(data, raw_data, *off);
2468c2ecf20Sopenharmony_ci	resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
2478c2ecf20Sopenharmony_ci	if (!resp || !resp->in_report)
2488c2ecf20Sopenharmony_ci		goto skip;
2498c2ecf20Sopenharmony_ci	if (resp->in_report->id == REPORT_MEMORY ||
2508c2ecf20Sopenharmony_ci		resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
2518c2ecf20Sopenharmony_ci		if (memcmp(raw_data, resp->raw_data, len_off) != 0)
2528c2ecf20Sopenharmony_ci			goto skip;
2538c2ecf20Sopenharmony_ci		ret = 0;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ciskip:
2568c2ecf20Sopenharmony_ci	kfree(resp);
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/* write a given size of data (bounds check to be done by caller) */
2618c2ecf20Sopenharmony_cistatic ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
2628c2ecf20Sopenharmony_ci		const char __user *u, size_t s, loff_t *off)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct picolcd_pending *resp;
2658c2ecf20Sopenharmony_ci	u8 raw_data[36];
2668c2ecf20Sopenharmony_ci	ssize_t ret = 0;
2678c2ecf20Sopenharmony_ci	int len_off, err = -EIO;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	while (s > 0) {
2708c2ecf20Sopenharmony_ci		err = -EIO;
2718c2ecf20Sopenharmony_ci		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
2728c2ecf20Sopenharmony_ci		raw_data[len_off] = s > 32 ? 32 : s;
2738c2ecf20Sopenharmony_ci		if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
2748c2ecf20Sopenharmony_ci			err = -EFAULT;
2758c2ecf20Sopenharmony_ci			break;
2768c2ecf20Sopenharmony_ci		}
2778c2ecf20Sopenharmony_ci		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
2788c2ecf20Sopenharmony_ci				len_off+1+raw_data[len_off]);
2798c2ecf20Sopenharmony_ci		if (!resp || !resp->in_report)
2808c2ecf20Sopenharmony_ci			goto skip;
2818c2ecf20Sopenharmony_ci		if (resp->in_report->id == REPORT_MEMORY ||
2828c2ecf20Sopenharmony_ci			resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
2838c2ecf20Sopenharmony_ci			if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
2848c2ecf20Sopenharmony_ci				goto skip;
2858c2ecf20Sopenharmony_ci			*off += raw_data[len_off];
2868c2ecf20Sopenharmony_ci			s    -= raw_data[len_off];
2878c2ecf20Sopenharmony_ci			ret  += raw_data[len_off];
2888c2ecf20Sopenharmony_ci			err   = 0;
2898c2ecf20Sopenharmony_ci		}
2908c2ecf20Sopenharmony_ciskip:
2918c2ecf20Sopenharmony_ci		kfree(resp);
2928c2ecf20Sopenharmony_ci		if (err)
2938c2ecf20Sopenharmony_ci			break;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci	return ret > 0 ? ret : err;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
2998c2ecf20Sopenharmony_ci		size_t s, loff_t *off)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct picolcd_data *data = f->private_data;
3028c2ecf20Sopenharmony_ci	ssize_t err, ret = 0;
3038c2ecf20Sopenharmony_ci	int report_erase, report_write;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (s == 0)
3068c2ecf20Sopenharmony_ci		return -EINVAL;
3078c2ecf20Sopenharmony_ci	if (*off > 0x5fff)
3088c2ecf20Sopenharmony_ci		return -ENOSPC;
3098c2ecf20Sopenharmony_ci	if (s & 0x3f)
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci	if (*off & 0x3f)
3128c2ecf20Sopenharmony_ci		return -EINVAL;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (data->status & PICOLCD_BOOTLOADER) {
3158c2ecf20Sopenharmony_ci		report_erase = REPORT_BL_ERASE_MEMORY;
3168c2ecf20Sopenharmony_ci		report_write = REPORT_BL_WRITE_MEMORY;
3178c2ecf20Sopenharmony_ci	} else {
3188c2ecf20Sopenharmony_ci		report_erase = REPORT_ERASE_MEMORY;
3198c2ecf20Sopenharmony_ci		report_write = REPORT_WRITE_MEMORY;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci	mutex_lock(&data->mutex_flash);
3228c2ecf20Sopenharmony_ci	while (s > 0) {
3238c2ecf20Sopenharmony_ci		err = _picolcd_flash_erase64(data, report_erase, off);
3248c2ecf20Sopenharmony_ci		if (err)
3258c2ecf20Sopenharmony_ci			break;
3268c2ecf20Sopenharmony_ci		err = _picolcd_flash_write(data, report_write, u, 64, off);
3278c2ecf20Sopenharmony_ci		if (err < 0)
3288c2ecf20Sopenharmony_ci			break;
3298c2ecf20Sopenharmony_ci		ret += err;
3308c2ecf20Sopenharmony_ci		*off += err;
3318c2ecf20Sopenharmony_ci		s -= err;
3328c2ecf20Sopenharmony_ci		if (err != 64)
3338c2ecf20Sopenharmony_ci			break;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci	mutex_unlock(&data->mutex_flash);
3368c2ecf20Sopenharmony_ci	return ret > 0 ? ret : err;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/*
3408c2ecf20Sopenharmony_ci * Notes:
3418c2ecf20Sopenharmony_ci * - concurrent writing is prevented by mutex and all writes must be
3428c2ecf20Sopenharmony_ci *   n*64 bytes and 64-byte aligned, each write being preceded by an
3438c2ecf20Sopenharmony_ci *   ERASE which erases a 64byte block.
3448c2ecf20Sopenharmony_ci *   If less than requested was written or an error is returned for an
3458c2ecf20Sopenharmony_ci *   otherwise correct write request the next 64-byte block which should
3468c2ecf20Sopenharmony_ci *   have been written is in undefined state (mostly: original, erased,
3478c2ecf20Sopenharmony_ci *   (half-)written with write error)
3488c2ecf20Sopenharmony_ci * - reading can happen without special restriction
3498c2ecf20Sopenharmony_ci */
3508c2ecf20Sopenharmony_cistatic const struct file_operations picolcd_debug_flash_fops = {
3518c2ecf20Sopenharmony_ci	.owner    = THIS_MODULE,
3528c2ecf20Sopenharmony_ci	.open     = simple_open,
3538c2ecf20Sopenharmony_ci	.read     = picolcd_debug_flash_read,
3548c2ecf20Sopenharmony_ci	.write    = picolcd_debug_flash_write,
3558c2ecf20Sopenharmony_ci	.llseek   = generic_file_llseek,
3568c2ecf20Sopenharmony_ci};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/*
3608c2ecf20Sopenharmony_ci * Helper code for HID report level dumping/debugging
3618c2ecf20Sopenharmony_ci */
3628c2ecf20Sopenharmony_cistatic const char * const error_codes[] = {
3638c2ecf20Sopenharmony_ci	"success", "parameter missing", "data_missing", "block readonly",
3648c2ecf20Sopenharmony_ci	"block not erasable", "block too big", "section overflow",
3658c2ecf20Sopenharmony_ci	"invalid command length", "invalid data length",
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
3698c2ecf20Sopenharmony_ci		const size_t data_len)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	int i, j;
3728c2ecf20Sopenharmony_ci	for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
3738c2ecf20Sopenharmony_ci		dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
3748c2ecf20Sopenharmony_ci		dst[j++] = hex_asc[data[i] & 0x0f];
3758c2ecf20Sopenharmony_ci		dst[j++] = ' ';
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	dst[j]   = '\0';
3788c2ecf20Sopenharmony_ci	if (j > 0)
3798c2ecf20Sopenharmony_ci		dst[j-1] = '\n';
3808c2ecf20Sopenharmony_ci	if (i < data_len && j > 2)
3818c2ecf20Sopenharmony_ci		dst[j-2] = dst[j-3] = '.';
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_civoid picolcd_debug_out_report(struct picolcd_data *data,
3858c2ecf20Sopenharmony_ci		struct hid_device *hdev, struct hid_report *report)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	u8 *raw_data;
3888c2ecf20Sopenharmony_ci	int raw_size = (report->size >> 3) + 1;
3898c2ecf20Sopenharmony_ci	char *buff;
3908c2ecf20Sopenharmony_ci#define BUFF_SZ 256
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Avoid unnecessary overhead if debugfs is disabled */
3938c2ecf20Sopenharmony_ci	if (list_empty(&hdev->debug_list))
3948c2ecf20Sopenharmony_ci		return;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
3978c2ecf20Sopenharmony_ci	if (!buff)
3988c2ecf20Sopenharmony_ci		return;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	raw_data = hid_alloc_report_buf(report, GFP_ATOMIC);
4018c2ecf20Sopenharmony_ci	if (!raw_data) {
4028c2ecf20Sopenharmony_ci		kfree(buff);
4038c2ecf20Sopenharmony_ci		return;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	snprintf(buff, BUFF_SZ, "\nout report %d (size %d) =  ",
4078c2ecf20Sopenharmony_ci			report->id, raw_size);
4088c2ecf20Sopenharmony_ci	hid_debug_event(hdev, buff);
4098c2ecf20Sopenharmony_ci	raw_data[0] = report->id;
4108c2ecf20Sopenharmony_ci	hid_output_report(report, raw_data);
4118c2ecf20Sopenharmony_ci	dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
4128c2ecf20Sopenharmony_ci	hid_debug_event(hdev, buff);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	switch (report->id) {
4158c2ecf20Sopenharmony_ci	case REPORT_LED_STATE:
4168c2ecf20Sopenharmony_ci		/* 1 data byte with GPO state */
4178c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4188c2ecf20Sopenharmony_ci			"REPORT_LED_STATE", report->id, raw_size-1);
4198c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4208c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
4218c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	case REPORT_BRIGHTNESS:
4248c2ecf20Sopenharmony_ci		/* 1 data byte with brightness */
4258c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4268c2ecf20Sopenharmony_ci			"REPORT_BRIGHTNESS", report->id, raw_size-1);
4278c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4288c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
4298c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4308c2ecf20Sopenharmony_ci		break;
4318c2ecf20Sopenharmony_ci	case REPORT_CONTRAST:
4328c2ecf20Sopenharmony_ci		/* 1 data byte with contrast */
4338c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4348c2ecf20Sopenharmony_ci			"REPORT_CONTRAST", report->id, raw_size-1);
4358c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4368c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
4378c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	case REPORT_RESET:
4408c2ecf20Sopenharmony_ci		/* 2 data bytes with reset duration in ms */
4418c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4428c2ecf20Sopenharmony_ci			"REPORT_RESET", report->id, raw_size-1);
4438c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4448c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
4458c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
4468c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci	case REPORT_LCD_CMD:
4498c2ecf20Sopenharmony_ci		/* 63 data bytes with LCD commands */
4508c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4518c2ecf20Sopenharmony_ci			"REPORT_LCD_CMD", report->id, raw_size-1);
4528c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4538c2ecf20Sopenharmony_ci		/* TODO: format decoding */
4548c2ecf20Sopenharmony_ci		break;
4558c2ecf20Sopenharmony_ci	case REPORT_LCD_DATA:
4568c2ecf20Sopenharmony_ci		/* 63 data bytes with LCD data */
4578c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4588c2ecf20Sopenharmony_ci			"REPORT_LCD_CMD", report->id, raw_size-1);
4598c2ecf20Sopenharmony_ci		/* TODO: format decoding */
4608c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	case REPORT_LCD_CMD_DATA:
4638c2ecf20Sopenharmony_ci		/* 63 data bytes with LCD commands and data */
4648c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4658c2ecf20Sopenharmony_ci			"REPORT_LCD_CMD", report->id, raw_size-1);
4668c2ecf20Sopenharmony_ci		/* TODO: format decoding */
4678c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4688c2ecf20Sopenharmony_ci		break;
4698c2ecf20Sopenharmony_ci	case REPORT_EE_READ:
4708c2ecf20Sopenharmony_ci		/* 3 data bytes with read area description */
4718c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4728c2ecf20Sopenharmony_ci			"REPORT_EE_READ", report->id, raw_size-1);
4738c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4748c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
4758c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1]);
4768c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4778c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
4788c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	case REPORT_EE_WRITE:
4818c2ecf20Sopenharmony_ci		/* 3+1..20 data bytes with write area description */
4828c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
4838c2ecf20Sopenharmony_ci			"REPORT_EE_WRITE", report->id, raw_size-1);
4848c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4858c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
4868c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1]);
4878c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4888c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
4898c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
4908c2ecf20Sopenharmony_ci		if (raw_data[3] == 0) {
4918c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNo data\n");
4928c2ecf20Sopenharmony_ci		} else if (raw_data[3] + 4 <= raw_size) {
4938c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData: ");
4948c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
4958c2ecf20Sopenharmony_ci			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
4968c2ecf20Sopenharmony_ci		} else {
4978c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
4988c2ecf20Sopenharmony_ci		}
4998c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5008c2ecf20Sopenharmony_ci		break;
5018c2ecf20Sopenharmony_ci	case REPORT_ERASE_MEMORY:
5028c2ecf20Sopenharmony_ci	case REPORT_BL_ERASE_MEMORY:
5038c2ecf20Sopenharmony_ci		/* 3 data bytes with pointer inside erase block */
5048c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
5058c2ecf20Sopenharmony_ci			"REPORT_ERASE_MEMORY", report->id, raw_size-1);
5068c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5078c2ecf20Sopenharmony_ci		switch (data->addr_sz) {
5088c2ecf20Sopenharmony_ci		case 2:
5098c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
5108c2ecf20Sopenharmony_ci					raw_data[2], raw_data[1]);
5118c2ecf20Sopenharmony_ci			break;
5128c2ecf20Sopenharmony_ci		case 3:
5138c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
5148c2ecf20Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
5158c2ecf20Sopenharmony_ci			break;
5168c2ecf20Sopenharmony_ci		default:
5178c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	case REPORT_READ_MEMORY:
5228c2ecf20Sopenharmony_ci	case REPORT_BL_READ_MEMORY:
5238c2ecf20Sopenharmony_ci		/* 4 data bytes with read area description */
5248c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
5258c2ecf20Sopenharmony_ci			"REPORT_READ_MEMORY", report->id, raw_size-1);
5268c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5278c2ecf20Sopenharmony_ci		switch (data->addr_sz) {
5288c2ecf20Sopenharmony_ci		case 2:
5298c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
5308c2ecf20Sopenharmony_ci					raw_data[2], raw_data[1]);
5318c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
5328c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
5338c2ecf20Sopenharmony_ci			break;
5348c2ecf20Sopenharmony_ci		case 3:
5358c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
5368c2ecf20Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
5378c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
5388c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
5398c2ecf20Sopenharmony_ci			break;
5408c2ecf20Sopenharmony_ci		default:
5418c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5448c2ecf20Sopenharmony_ci		break;
5458c2ecf20Sopenharmony_ci	case REPORT_WRITE_MEMORY:
5468c2ecf20Sopenharmony_ci	case REPORT_BL_WRITE_MEMORY:
5478c2ecf20Sopenharmony_ci		/* 4+1..32 data bytes with write adrea description */
5488c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
5498c2ecf20Sopenharmony_ci			"REPORT_WRITE_MEMORY", report->id, raw_size-1);
5508c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5518c2ecf20Sopenharmony_ci		switch (data->addr_sz) {
5528c2ecf20Sopenharmony_ci		case 2:
5538c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
5548c2ecf20Sopenharmony_ci					raw_data[2], raw_data[1]);
5558c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
5568c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
5578c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
5588c2ecf20Sopenharmony_ci			if (raw_data[3] == 0) {
5598c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
5608c2ecf20Sopenharmony_ci			} else if (raw_data[3] + 4 <= raw_size) {
5618c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
5628c2ecf20Sopenharmony_ci				hid_debug_event(hdev, buff);
5638c2ecf20Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
5648c2ecf20Sopenharmony_ci			} else {
5658c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
5668c2ecf20Sopenharmony_ci			}
5678c2ecf20Sopenharmony_ci			break;
5688c2ecf20Sopenharmony_ci		case 3:
5698c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
5708c2ecf20Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
5718c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
5728c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
5738c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
5748c2ecf20Sopenharmony_ci			if (raw_data[4] == 0) {
5758c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
5768c2ecf20Sopenharmony_ci			} else if (raw_data[4] + 5 <= raw_size) {
5778c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
5788c2ecf20Sopenharmony_ci				hid_debug_event(hdev, buff);
5798c2ecf20Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
5808c2ecf20Sopenharmony_ci			} else {
5818c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
5828c2ecf20Sopenharmony_ci			}
5838c2ecf20Sopenharmony_ci			break;
5848c2ecf20Sopenharmony_ci		default:
5858c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5888c2ecf20Sopenharmony_ci		break;
5898c2ecf20Sopenharmony_ci	case REPORT_SPLASH_RESTART:
5908c2ecf20Sopenharmony_ci		/* TODO */
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci	case REPORT_EXIT_KEYBOARD:
5938c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
5948c2ecf20Sopenharmony_ci			"REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
5958c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
5968c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
5978c2ecf20Sopenharmony_ci				raw_data[1] | (raw_data[2] << 8),
5988c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1]);
5998c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6008c2ecf20Sopenharmony_ci		break;
6018c2ecf20Sopenharmony_ci	case REPORT_VERSION:
6028c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
6038c2ecf20Sopenharmony_ci			"REPORT_VERSION", report->id, raw_size-1);
6048c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6058c2ecf20Sopenharmony_ci		break;
6068c2ecf20Sopenharmony_ci	case REPORT_DEVID:
6078c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
6088c2ecf20Sopenharmony_ci			"REPORT_DEVID", report->id, raw_size-1);
6098c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6108c2ecf20Sopenharmony_ci		break;
6118c2ecf20Sopenharmony_ci	case REPORT_SPLASH_SIZE:
6128c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
6138c2ecf20Sopenharmony_ci			"REPORT_SPLASH_SIZE", report->id, raw_size-1);
6148c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6158c2ecf20Sopenharmony_ci		break;
6168c2ecf20Sopenharmony_ci	case REPORT_HOOK_VERSION:
6178c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
6188c2ecf20Sopenharmony_ci			"REPORT_HOOK_VERSION", report->id, raw_size-1);
6198c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6208c2ecf20Sopenharmony_ci		break;
6218c2ecf20Sopenharmony_ci	case REPORT_EXIT_FLASHER:
6228c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
6238c2ecf20Sopenharmony_ci			"REPORT_VERSION", report->id, raw_size-1);
6248c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6258c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
6268c2ecf20Sopenharmony_ci				raw_data[1] | (raw_data[2] << 8),
6278c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1]);
6288c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6298c2ecf20Sopenharmony_ci		break;
6308c2ecf20Sopenharmony_ci	default:
6318c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
6328c2ecf20Sopenharmony_ci			"<unknown>", report->id, raw_size-1);
6338c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6348c2ecf20Sopenharmony_ci		break;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci	wake_up_interruptible(&hdev->debug_wait);
6378c2ecf20Sopenharmony_ci	kfree(raw_data);
6388c2ecf20Sopenharmony_ci	kfree(buff);
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_civoid picolcd_debug_raw_event(struct picolcd_data *data,
6428c2ecf20Sopenharmony_ci		struct hid_device *hdev, struct hid_report *report,
6438c2ecf20Sopenharmony_ci		u8 *raw_data, int size)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	char *buff;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci#define BUFF_SZ 256
6488c2ecf20Sopenharmony_ci	/* Avoid unnecessary overhead if debugfs is disabled */
6498c2ecf20Sopenharmony_ci	if (list_empty(&hdev->debug_list))
6508c2ecf20Sopenharmony_ci		return;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
6538c2ecf20Sopenharmony_ci	if (!buff)
6548c2ecf20Sopenharmony_ci		return;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	switch (report->id) {
6578c2ecf20Sopenharmony_ci	case REPORT_ERROR_CODE:
6588c2ecf20Sopenharmony_ci		/* 2 data bytes with affected report and error code */
6598c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
6608c2ecf20Sopenharmony_ci			"REPORT_ERROR_CODE", report->id, size-1);
6618c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6628c2ecf20Sopenharmony_ci		if (raw_data[2] < ARRAY_SIZE(error_codes))
6638c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
6648c2ecf20Sopenharmony_ci					raw_data[2], error_codes[raw_data[2]], raw_data[1]);
6658c2ecf20Sopenharmony_ci		else
6668c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
6678c2ecf20Sopenharmony_ci					raw_data[2], raw_data[1]);
6688c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6698c2ecf20Sopenharmony_ci		break;
6708c2ecf20Sopenharmony_ci	case REPORT_KEY_STATE:
6718c2ecf20Sopenharmony_ci		/* 2 data bytes with key state */
6728c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
6738c2ecf20Sopenharmony_ci			"REPORT_KEY_STATE", report->id, size-1);
6748c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6758c2ecf20Sopenharmony_ci		if (raw_data[1] == 0)
6768c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
6778c2ecf20Sopenharmony_ci		else if (raw_data[2] == 0)
6788c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
6798c2ecf20Sopenharmony_ci					raw_data[1], raw_data[1]);
6808c2ecf20Sopenharmony_ci		else
6818c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
6828c2ecf20Sopenharmony_ci					raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
6838c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6848c2ecf20Sopenharmony_ci		break;
6858c2ecf20Sopenharmony_ci	case REPORT_IR_DATA:
6868c2ecf20Sopenharmony_ci		/* Up to 20 byes of IR scancode data */
6878c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
6888c2ecf20Sopenharmony_ci			"REPORT_IR_DATA", report->id, size-1);
6898c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
6908c2ecf20Sopenharmony_ci		if (raw_data[1] == 0) {
6918c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
6928c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
6938c2ecf20Sopenharmony_ci		} else if (raw_data[1] + 1 <= size) {
6948c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
6958c2ecf20Sopenharmony_ci					raw_data[1]);
6968c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
6978c2ecf20Sopenharmony_ci			dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
6988c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
6998c2ecf20Sopenharmony_ci		} else {
7008c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
7018c2ecf20Sopenharmony_ci					raw_data[1]-1);
7028c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7038c2ecf20Sopenharmony_ci		}
7048c2ecf20Sopenharmony_ci		break;
7058c2ecf20Sopenharmony_ci	case REPORT_EE_DATA:
7068c2ecf20Sopenharmony_ci		/* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
7078c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7088c2ecf20Sopenharmony_ci			"REPORT_EE_DATA", report->id, size-1);
7098c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7108c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
7118c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1]);
7128c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7138c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
7148c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7158c2ecf20Sopenharmony_ci		if (raw_data[3] == 0) {
7168c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNo data\n");
7178c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7188c2ecf20Sopenharmony_ci		} else if (raw_data[3] + 4 <= size) {
7198c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData: ");
7208c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7218c2ecf20Sopenharmony_ci			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
7228c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7238c2ecf20Sopenharmony_ci		} else {
7248c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
7258c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7268c2ecf20Sopenharmony_ci		}
7278c2ecf20Sopenharmony_ci		break;
7288c2ecf20Sopenharmony_ci	case REPORT_MEMORY:
7298c2ecf20Sopenharmony_ci		/* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRITE_MEMORY */
7308c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7318c2ecf20Sopenharmony_ci			"REPORT_MEMORY", report->id, size-1);
7328c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7338c2ecf20Sopenharmony_ci		switch (data->addr_sz) {
7348c2ecf20Sopenharmony_ci		case 2:
7358c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
7368c2ecf20Sopenharmony_ci					raw_data[2], raw_data[1]);
7378c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7388c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
7398c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7408c2ecf20Sopenharmony_ci			if (raw_data[3] == 0) {
7418c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
7428c2ecf20Sopenharmony_ci			} else if (raw_data[3] + 4 <= size) {
7438c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
7448c2ecf20Sopenharmony_ci				hid_debug_event(hdev, buff);
7458c2ecf20Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
7468c2ecf20Sopenharmony_ci			} else {
7478c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
7488c2ecf20Sopenharmony_ci			}
7498c2ecf20Sopenharmony_ci			break;
7508c2ecf20Sopenharmony_ci		case 3:
7518c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
7528c2ecf20Sopenharmony_ci					raw_data[3], raw_data[2], raw_data[1]);
7538c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7548c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
7558c2ecf20Sopenharmony_ci			hid_debug_event(hdev, buff);
7568c2ecf20Sopenharmony_ci			if (raw_data[4] == 0) {
7578c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tNo data\n");
7588c2ecf20Sopenharmony_ci			} else if (raw_data[4] + 5 <= size) {
7598c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData: ");
7608c2ecf20Sopenharmony_ci				hid_debug_event(hdev, buff);
7618c2ecf20Sopenharmony_ci				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
7628c2ecf20Sopenharmony_ci			} else {
7638c2ecf20Sopenharmony_ci				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
7648c2ecf20Sopenharmony_ci			}
7658c2ecf20Sopenharmony_ci			break;
7668c2ecf20Sopenharmony_ci		default:
7678c2ecf20Sopenharmony_ci			snprintf(buff, BUFF_SZ, "\tNot supported\n");
7688c2ecf20Sopenharmony_ci		}
7698c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7708c2ecf20Sopenharmony_ci		break;
7718c2ecf20Sopenharmony_ci	case REPORT_VERSION:
7728c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7738c2ecf20Sopenharmony_ci			"REPORT_VERSION", report->id, size-1);
7748c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7758c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
7768c2ecf20Sopenharmony_ci				raw_data[2], raw_data[1]);
7778c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7788c2ecf20Sopenharmony_ci		break;
7798c2ecf20Sopenharmony_ci	case REPORT_BL_ERASE_MEMORY:
7808c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7818c2ecf20Sopenharmony_ci			"REPORT_BL_ERASE_MEMORY", report->id, size-1);
7828c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7838c2ecf20Sopenharmony_ci		/* TODO */
7848c2ecf20Sopenharmony_ci		break;
7858c2ecf20Sopenharmony_ci	case REPORT_BL_READ_MEMORY:
7868c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7878c2ecf20Sopenharmony_ci			"REPORT_BL_READ_MEMORY", report->id, size-1);
7888c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7898c2ecf20Sopenharmony_ci		/* TODO */
7908c2ecf20Sopenharmony_ci		break;
7918c2ecf20Sopenharmony_ci	case REPORT_BL_WRITE_MEMORY:
7928c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7938c2ecf20Sopenharmony_ci			"REPORT_BL_WRITE_MEMORY", report->id, size-1);
7948c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
7958c2ecf20Sopenharmony_ci		/* TODO */
7968c2ecf20Sopenharmony_ci		break;
7978c2ecf20Sopenharmony_ci	case REPORT_DEVID:
7988c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
7998c2ecf20Sopenharmony_ci			"REPORT_DEVID", report->id, size-1);
8008c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8018c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
8028c2ecf20Sopenharmony_ci				raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
8038c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8048c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
8058c2ecf20Sopenharmony_ci				raw_data[5]);
8068c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8078c2ecf20Sopenharmony_ci		break;
8088c2ecf20Sopenharmony_ci	case REPORT_SPLASH_SIZE:
8098c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
8108c2ecf20Sopenharmony_ci			"REPORT_SPLASH_SIZE", report->id, size-1);
8118c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8128c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
8138c2ecf20Sopenharmony_ci				(raw_data[2] << 8) | raw_data[1]);
8148c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8158c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
8168c2ecf20Sopenharmony_ci				(raw_data[4] << 8) | raw_data[3]);
8178c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8188c2ecf20Sopenharmony_ci		break;
8198c2ecf20Sopenharmony_ci	case REPORT_HOOK_VERSION:
8208c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
8218c2ecf20Sopenharmony_ci			"REPORT_HOOK_VERSION", report->id, size-1);
8228c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8238c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
8248c2ecf20Sopenharmony_ci				raw_data[1], raw_data[2]);
8258c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8268c2ecf20Sopenharmony_ci		break;
8278c2ecf20Sopenharmony_ci	default:
8288c2ecf20Sopenharmony_ci		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
8298c2ecf20Sopenharmony_ci			"<unknown>", report->id, size-1);
8308c2ecf20Sopenharmony_ci		hid_debug_event(hdev, buff);
8318c2ecf20Sopenharmony_ci		break;
8328c2ecf20Sopenharmony_ci	}
8338c2ecf20Sopenharmony_ci	wake_up_interruptible(&hdev->debug_wait);
8348c2ecf20Sopenharmony_ci	kfree(buff);
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_civoid picolcd_init_devfs(struct picolcd_data *data,
8388c2ecf20Sopenharmony_ci		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
8398c2ecf20Sopenharmony_ci		struct hid_report *flash_r, struct hid_report *flash_w,
8408c2ecf20Sopenharmony_ci		struct hid_report *reset)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	struct hid_device *hdev = data->hdev;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	mutex_init(&data->mutex_flash);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* reset */
8478c2ecf20Sopenharmony_ci	if (reset)
8488c2ecf20Sopenharmony_ci		data->debug_reset = debugfs_create_file("reset", 0600,
8498c2ecf20Sopenharmony_ci				hdev->debug_dir, data, &picolcd_debug_reset_fops);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/* eeprom */
8528c2ecf20Sopenharmony_ci	if (eeprom_r || eeprom_w)
8538c2ecf20Sopenharmony_ci		data->debug_eeprom = debugfs_create_file("eeprom",
8548c2ecf20Sopenharmony_ci			(eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
8558c2ecf20Sopenharmony_ci			hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	/* flash */
8588c2ecf20Sopenharmony_ci	if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
8598c2ecf20Sopenharmony_ci		data->addr_sz = flash_r->field[0]->report_count - 1;
8608c2ecf20Sopenharmony_ci	else
8618c2ecf20Sopenharmony_ci		data->addr_sz = -1;
8628c2ecf20Sopenharmony_ci	if (data->addr_sz == 2 || data->addr_sz == 3) {
8638c2ecf20Sopenharmony_ci		data->debug_flash = debugfs_create_file("flash",
8648c2ecf20Sopenharmony_ci			(flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
8658c2ecf20Sopenharmony_ci			hdev->debug_dir, data, &picolcd_debug_flash_fops);
8668c2ecf20Sopenharmony_ci	} else if (flash_r || flash_w)
8678c2ecf20Sopenharmony_ci		hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_civoid picolcd_exit_devfs(struct picolcd_data *data)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct dentry *dent;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	dent = data->debug_reset;
8758c2ecf20Sopenharmony_ci	data->debug_reset = NULL;
8768c2ecf20Sopenharmony_ci	debugfs_remove(dent);
8778c2ecf20Sopenharmony_ci	dent = data->debug_eeprom;
8788c2ecf20Sopenharmony_ci	data->debug_eeprom = NULL;
8798c2ecf20Sopenharmony_ci	debugfs_remove(dent);
8808c2ecf20Sopenharmony_ci	dent = data->debug_flash;
8818c2ecf20Sopenharmony_ci	data->debug_flash = NULL;
8828c2ecf20Sopenharmony_ci	debugfs_remove(dent);
8838c2ecf20Sopenharmony_ci	mutex_destroy(&data->mutex_flash);
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
886