xref: /OK3568_Linux_fs/kernel/drivers/hid/hid-picolcd_debugfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /***************************************************************************
3*4882a593Smuzhiyun  *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
4*4882a593Smuzhiyun  *                                                                         *
5*4882a593Smuzhiyun  *   Based on Logitech G13 driver (v0.4)                                   *
6*4882a593Smuzhiyun  *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
7*4882a593Smuzhiyun  *                                                                         *
8*4882a593Smuzhiyun  ***************************************************************************/
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/hid.h>
11*4882a593Smuzhiyun #include <linux/hid-debug.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/fb.h>
14*4882a593Smuzhiyun #include <linux/seq_file.h>
15*4882a593Smuzhiyun #include <linux/debugfs.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/uaccess.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "hid-picolcd.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 
picolcd_debug_reset_show(struct seq_file * f,void * p)23*4882a593Smuzhiyun static int picolcd_debug_reset_show(struct seq_file *f, void *p)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	if (picolcd_fbinfo((struct picolcd_data *)f->private))
26*4882a593Smuzhiyun 		seq_printf(f, "all fb\n");
27*4882a593Smuzhiyun 	else
28*4882a593Smuzhiyun 		seq_printf(f, "all\n");
29*4882a593Smuzhiyun 	return 0;
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun 
picolcd_debug_reset_open(struct inode * inode,struct file * f)32*4882a593Smuzhiyun static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	return single_open(f, picolcd_debug_reset_show, inode->i_private);
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun 
picolcd_debug_reset_write(struct file * f,const char __user * user_buf,size_t count,loff_t * ppos)37*4882a593Smuzhiyun static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
38*4882a593Smuzhiyun 		size_t count, loff_t *ppos)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
41*4882a593Smuzhiyun 	char buf[32];
42*4882a593Smuzhiyun 	size_t cnt = min(count, sizeof(buf)-1);
43*4882a593Smuzhiyun 	if (copy_from_user(buf, user_buf, cnt))
44*4882a593Smuzhiyun 		return -EFAULT;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
47*4882a593Smuzhiyun 		cnt--;
48*4882a593Smuzhiyun 	buf[cnt] = '\0';
49*4882a593Smuzhiyun 	if (strcmp(buf, "all") == 0) {
50*4882a593Smuzhiyun 		picolcd_reset(data->hdev);
51*4882a593Smuzhiyun 		picolcd_fb_reset(data, 1);
52*4882a593Smuzhiyun 	} else if (strcmp(buf, "fb") == 0) {
53*4882a593Smuzhiyun 		picolcd_fb_reset(data, 1);
54*4882a593Smuzhiyun 	} else {
55*4882a593Smuzhiyun 		return -EINVAL;
56*4882a593Smuzhiyun 	}
57*4882a593Smuzhiyun 	return count;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun static const struct file_operations picolcd_debug_reset_fops = {
61*4882a593Smuzhiyun 	.owner    = THIS_MODULE,
62*4882a593Smuzhiyun 	.open     = picolcd_debug_reset_open,
63*4882a593Smuzhiyun 	.read     = seq_read,
64*4882a593Smuzhiyun 	.llseek   = seq_lseek,
65*4882a593Smuzhiyun 	.write    = picolcd_debug_reset_write,
66*4882a593Smuzhiyun 	.release  = single_release,
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun /*
70*4882a593Smuzhiyun  * The "eeprom" file
71*4882a593Smuzhiyun  */
picolcd_debug_eeprom_read(struct file * f,char __user * u,size_t s,loff_t * off)72*4882a593Smuzhiyun static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
73*4882a593Smuzhiyun 		size_t s, loff_t *off)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	struct picolcd_data *data = f->private_data;
76*4882a593Smuzhiyun 	struct picolcd_pending *resp;
77*4882a593Smuzhiyun 	u8 raw_data[3];
78*4882a593Smuzhiyun 	ssize_t ret = -EIO;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	if (s == 0)
81*4882a593Smuzhiyun 		return -EINVAL;
82*4882a593Smuzhiyun 	if (*off > 0x0ff)
83*4882a593Smuzhiyun 		return 0;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	/* prepare buffer with info about what we want to read (addr & len) */
86*4882a593Smuzhiyun 	raw_data[0] = *off & 0xff;
87*4882a593Smuzhiyun 	raw_data[1] = (*off >> 8) & 0xff;
88*4882a593Smuzhiyun 	raw_data[2] = s < 20 ? s : 20;
89*4882a593Smuzhiyun 	if (*off + raw_data[2] > 0xff)
90*4882a593Smuzhiyun 		raw_data[2] = 0x100 - *off;
91*4882a593Smuzhiyun 	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
92*4882a593Smuzhiyun 			sizeof(raw_data));
93*4882a593Smuzhiyun 	if (!resp)
94*4882a593Smuzhiyun 		return -EIO;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
97*4882a593Smuzhiyun 		/* successful read :) */
98*4882a593Smuzhiyun 		ret = resp->raw_data[2];
99*4882a593Smuzhiyun 		if (ret > s)
100*4882a593Smuzhiyun 			ret = s;
101*4882a593Smuzhiyun 		if (copy_to_user(u, resp->raw_data+3, ret))
102*4882a593Smuzhiyun 			ret = -EFAULT;
103*4882a593Smuzhiyun 		else
104*4882a593Smuzhiyun 			*off += ret;
105*4882a593Smuzhiyun 	} /* anything else is some kind of IO error */
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	kfree(resp);
108*4882a593Smuzhiyun 	return ret;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
picolcd_debug_eeprom_write(struct file * f,const char __user * u,size_t s,loff_t * off)111*4882a593Smuzhiyun static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
112*4882a593Smuzhiyun 		size_t s, loff_t *off)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	struct picolcd_data *data = f->private_data;
115*4882a593Smuzhiyun 	struct picolcd_pending *resp;
116*4882a593Smuzhiyun 	ssize_t ret = -EIO;
117*4882a593Smuzhiyun 	u8 raw_data[23];
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if (s == 0)
120*4882a593Smuzhiyun 		return -EINVAL;
121*4882a593Smuzhiyun 	if (*off > 0x0ff)
122*4882a593Smuzhiyun 		return -ENOSPC;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	memset(raw_data, 0, sizeof(raw_data));
125*4882a593Smuzhiyun 	raw_data[0] = *off & 0xff;
126*4882a593Smuzhiyun 	raw_data[1] = (*off >> 8) & 0xff;
127*4882a593Smuzhiyun 	raw_data[2] = min_t(size_t, 20, s);
128*4882a593Smuzhiyun 	if (*off + raw_data[2] > 0xff)
129*4882a593Smuzhiyun 		raw_data[2] = 0x100 - *off;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
132*4882a593Smuzhiyun 		return -EFAULT;
133*4882a593Smuzhiyun 	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
134*4882a593Smuzhiyun 			sizeof(raw_data));
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	if (!resp)
137*4882a593Smuzhiyun 		return -EIO;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
140*4882a593Smuzhiyun 		/* check if written data matches */
141*4882a593Smuzhiyun 		if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
142*4882a593Smuzhiyun 			*off += raw_data[2];
143*4882a593Smuzhiyun 			ret = raw_data[2];
144*4882a593Smuzhiyun 		}
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun 	kfree(resp);
147*4882a593Smuzhiyun 	return ret;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun /*
151*4882a593Smuzhiyun  * Notes:
152*4882a593Smuzhiyun  * - read/write happens in chunks of at most 20 bytes, it's up to userspace
153*4882a593Smuzhiyun  *   to loop in order to get more data.
154*4882a593Smuzhiyun  * - on write errors on otherwise correct write request the bytes
155*4882a593Smuzhiyun  *   that should have been written are in undefined state.
156*4882a593Smuzhiyun  */
157*4882a593Smuzhiyun static const struct file_operations picolcd_debug_eeprom_fops = {
158*4882a593Smuzhiyun 	.owner    = THIS_MODULE,
159*4882a593Smuzhiyun 	.open     = simple_open,
160*4882a593Smuzhiyun 	.read     = picolcd_debug_eeprom_read,
161*4882a593Smuzhiyun 	.write    = picolcd_debug_eeprom_write,
162*4882a593Smuzhiyun 	.llseek   = generic_file_llseek,
163*4882a593Smuzhiyun };
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun /*
166*4882a593Smuzhiyun  * The "flash" file
167*4882a593Smuzhiyun  */
168*4882a593Smuzhiyun /* record a flash address to buf (bounds check to be done by caller) */
_picolcd_flash_setaddr(struct picolcd_data * data,u8 * buf,long off)169*4882a593Smuzhiyun static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	buf[0] = off & 0xff;
172*4882a593Smuzhiyun 	buf[1] = (off >> 8) & 0xff;
173*4882a593Smuzhiyun 	if (data->addr_sz == 3)
174*4882a593Smuzhiyun 		buf[2] = (off >> 16) & 0xff;
175*4882a593Smuzhiyun 	return data->addr_sz == 2 ? 2 : 3;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun /* read a given size of data (bounds check to be done by caller) */
_picolcd_flash_read(struct picolcd_data * data,int report_id,char __user * u,size_t s,loff_t * off)179*4882a593Smuzhiyun static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
180*4882a593Smuzhiyun 		char __user *u, size_t s, loff_t *off)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun 	struct picolcd_pending *resp;
183*4882a593Smuzhiyun 	u8 raw_data[4];
184*4882a593Smuzhiyun 	ssize_t ret = 0;
185*4882a593Smuzhiyun 	int len_off, err = -EIO;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	while (s > 0) {
188*4882a593Smuzhiyun 		err = -EIO;
189*4882a593Smuzhiyun 		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
190*4882a593Smuzhiyun 		raw_data[len_off] = s > 32 ? 32 : s;
191*4882a593Smuzhiyun 		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
192*4882a593Smuzhiyun 		if (!resp || !resp->in_report)
193*4882a593Smuzhiyun 			goto skip;
194*4882a593Smuzhiyun 		if (resp->in_report->id == REPORT_MEMORY ||
195*4882a593Smuzhiyun 			resp->in_report->id == REPORT_BL_READ_MEMORY) {
196*4882a593Smuzhiyun 			if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
197*4882a593Smuzhiyun 				goto skip;
198*4882a593Smuzhiyun 			if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
199*4882a593Smuzhiyun 				err = -EFAULT;
200*4882a593Smuzhiyun 				goto skip;
201*4882a593Smuzhiyun 			}
202*4882a593Smuzhiyun 			*off += raw_data[len_off];
203*4882a593Smuzhiyun 			s    -= raw_data[len_off];
204*4882a593Smuzhiyun 			ret  += raw_data[len_off];
205*4882a593Smuzhiyun 			err   = 0;
206*4882a593Smuzhiyun 		}
207*4882a593Smuzhiyun skip:
208*4882a593Smuzhiyun 		kfree(resp);
209*4882a593Smuzhiyun 		if (err)
210*4882a593Smuzhiyun 			return ret > 0 ? ret : err;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 	return ret;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
picolcd_debug_flash_read(struct file * f,char __user * u,size_t s,loff_t * off)215*4882a593Smuzhiyun static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
216*4882a593Smuzhiyun 		size_t s, loff_t *off)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	struct picolcd_data *data = f->private_data;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	if (s == 0)
221*4882a593Smuzhiyun 		return -EINVAL;
222*4882a593Smuzhiyun 	if (*off > 0x05fff)
223*4882a593Smuzhiyun 		return 0;
224*4882a593Smuzhiyun 	if (*off + s > 0x05fff)
225*4882a593Smuzhiyun 		s = 0x06000 - *off;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	if (data->status & PICOLCD_BOOTLOADER)
228*4882a593Smuzhiyun 		return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
229*4882a593Smuzhiyun 	else
230*4882a593Smuzhiyun 		return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun /* erase block aligned to 64bytes boundary */
_picolcd_flash_erase64(struct picolcd_data * data,int report_id,loff_t * off)234*4882a593Smuzhiyun static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
235*4882a593Smuzhiyun 		loff_t *off)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	struct picolcd_pending *resp;
238*4882a593Smuzhiyun 	u8 raw_data[3];
239*4882a593Smuzhiyun 	int len_off;
240*4882a593Smuzhiyun 	ssize_t ret = -EIO;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	if (*off & 0x3f)
243*4882a593Smuzhiyun 		return -EINVAL;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	len_off = _picolcd_flash_setaddr(data, raw_data, *off);
246*4882a593Smuzhiyun 	resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
247*4882a593Smuzhiyun 	if (!resp || !resp->in_report)
248*4882a593Smuzhiyun 		goto skip;
249*4882a593Smuzhiyun 	if (resp->in_report->id == REPORT_MEMORY ||
250*4882a593Smuzhiyun 		resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
251*4882a593Smuzhiyun 		if (memcmp(raw_data, resp->raw_data, len_off) != 0)
252*4882a593Smuzhiyun 			goto skip;
253*4882a593Smuzhiyun 		ret = 0;
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun skip:
256*4882a593Smuzhiyun 	kfree(resp);
257*4882a593Smuzhiyun 	return ret;
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun /* write a given size of data (bounds check to be done by caller) */
_picolcd_flash_write(struct picolcd_data * data,int report_id,const char __user * u,size_t s,loff_t * off)261*4882a593Smuzhiyun static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
262*4882a593Smuzhiyun 		const char __user *u, size_t s, loff_t *off)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	struct picolcd_pending *resp;
265*4882a593Smuzhiyun 	u8 raw_data[36];
266*4882a593Smuzhiyun 	ssize_t ret = 0;
267*4882a593Smuzhiyun 	int len_off, err = -EIO;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	while (s > 0) {
270*4882a593Smuzhiyun 		err = -EIO;
271*4882a593Smuzhiyun 		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
272*4882a593Smuzhiyun 		raw_data[len_off] = s > 32 ? 32 : s;
273*4882a593Smuzhiyun 		if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
274*4882a593Smuzhiyun 			err = -EFAULT;
275*4882a593Smuzhiyun 			break;
276*4882a593Smuzhiyun 		}
277*4882a593Smuzhiyun 		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
278*4882a593Smuzhiyun 				len_off+1+raw_data[len_off]);
279*4882a593Smuzhiyun 		if (!resp || !resp->in_report)
280*4882a593Smuzhiyun 			goto skip;
281*4882a593Smuzhiyun 		if (resp->in_report->id == REPORT_MEMORY ||
282*4882a593Smuzhiyun 			resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
283*4882a593Smuzhiyun 			if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
284*4882a593Smuzhiyun 				goto skip;
285*4882a593Smuzhiyun 			*off += raw_data[len_off];
286*4882a593Smuzhiyun 			s    -= raw_data[len_off];
287*4882a593Smuzhiyun 			ret  += raw_data[len_off];
288*4882a593Smuzhiyun 			err   = 0;
289*4882a593Smuzhiyun 		}
290*4882a593Smuzhiyun skip:
291*4882a593Smuzhiyun 		kfree(resp);
292*4882a593Smuzhiyun 		if (err)
293*4882a593Smuzhiyun 			break;
294*4882a593Smuzhiyun 	}
295*4882a593Smuzhiyun 	return ret > 0 ? ret : err;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun 
picolcd_debug_flash_write(struct file * f,const char __user * u,size_t s,loff_t * off)298*4882a593Smuzhiyun static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
299*4882a593Smuzhiyun 		size_t s, loff_t *off)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	struct picolcd_data *data = f->private_data;
302*4882a593Smuzhiyun 	ssize_t err, ret = 0;
303*4882a593Smuzhiyun 	int report_erase, report_write;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	if (s == 0)
306*4882a593Smuzhiyun 		return -EINVAL;
307*4882a593Smuzhiyun 	if (*off > 0x5fff)
308*4882a593Smuzhiyun 		return -ENOSPC;
309*4882a593Smuzhiyun 	if (s & 0x3f)
310*4882a593Smuzhiyun 		return -EINVAL;
311*4882a593Smuzhiyun 	if (*off & 0x3f)
312*4882a593Smuzhiyun 		return -EINVAL;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	if (data->status & PICOLCD_BOOTLOADER) {
315*4882a593Smuzhiyun 		report_erase = REPORT_BL_ERASE_MEMORY;
316*4882a593Smuzhiyun 		report_write = REPORT_BL_WRITE_MEMORY;
317*4882a593Smuzhiyun 	} else {
318*4882a593Smuzhiyun 		report_erase = REPORT_ERASE_MEMORY;
319*4882a593Smuzhiyun 		report_write = REPORT_WRITE_MEMORY;
320*4882a593Smuzhiyun 	}
321*4882a593Smuzhiyun 	mutex_lock(&data->mutex_flash);
322*4882a593Smuzhiyun 	while (s > 0) {
323*4882a593Smuzhiyun 		err = _picolcd_flash_erase64(data, report_erase, off);
324*4882a593Smuzhiyun 		if (err)
325*4882a593Smuzhiyun 			break;
326*4882a593Smuzhiyun 		err = _picolcd_flash_write(data, report_write, u, 64, off);
327*4882a593Smuzhiyun 		if (err < 0)
328*4882a593Smuzhiyun 			break;
329*4882a593Smuzhiyun 		ret += err;
330*4882a593Smuzhiyun 		*off += err;
331*4882a593Smuzhiyun 		s -= err;
332*4882a593Smuzhiyun 		if (err != 64)
333*4882a593Smuzhiyun 			break;
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun 	mutex_unlock(&data->mutex_flash);
336*4882a593Smuzhiyun 	return ret > 0 ? ret : err;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun /*
340*4882a593Smuzhiyun  * Notes:
341*4882a593Smuzhiyun  * - concurrent writing is prevented by mutex and all writes must be
342*4882a593Smuzhiyun  *   n*64 bytes and 64-byte aligned, each write being preceded by an
343*4882a593Smuzhiyun  *   ERASE which erases a 64byte block.
344*4882a593Smuzhiyun  *   If less than requested was written or an error is returned for an
345*4882a593Smuzhiyun  *   otherwise correct write request the next 64-byte block which should
346*4882a593Smuzhiyun  *   have been written is in undefined state (mostly: original, erased,
347*4882a593Smuzhiyun  *   (half-)written with write error)
348*4882a593Smuzhiyun  * - reading can happen without special restriction
349*4882a593Smuzhiyun  */
350*4882a593Smuzhiyun static const struct file_operations picolcd_debug_flash_fops = {
351*4882a593Smuzhiyun 	.owner    = THIS_MODULE,
352*4882a593Smuzhiyun 	.open     = simple_open,
353*4882a593Smuzhiyun 	.read     = picolcd_debug_flash_read,
354*4882a593Smuzhiyun 	.write    = picolcd_debug_flash_write,
355*4882a593Smuzhiyun 	.llseek   = generic_file_llseek,
356*4882a593Smuzhiyun };
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun /*
360*4882a593Smuzhiyun  * Helper code for HID report level dumping/debugging
361*4882a593Smuzhiyun  */
362*4882a593Smuzhiyun static const char * const error_codes[] = {
363*4882a593Smuzhiyun 	"success", "parameter missing", "data_missing", "block readonly",
364*4882a593Smuzhiyun 	"block not erasable", "block too big", "section overflow",
365*4882a593Smuzhiyun 	"invalid command length", "invalid data length",
366*4882a593Smuzhiyun };
367*4882a593Smuzhiyun 
dump_buff_as_hex(char * dst,size_t dst_sz,const u8 * data,const size_t data_len)368*4882a593Smuzhiyun static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
369*4882a593Smuzhiyun 		const size_t data_len)
370*4882a593Smuzhiyun {
371*4882a593Smuzhiyun 	int i, j;
372*4882a593Smuzhiyun 	for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
373*4882a593Smuzhiyun 		dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
374*4882a593Smuzhiyun 		dst[j++] = hex_asc[data[i] & 0x0f];
375*4882a593Smuzhiyun 		dst[j++] = ' ';
376*4882a593Smuzhiyun 	}
377*4882a593Smuzhiyun 	dst[j]   = '\0';
378*4882a593Smuzhiyun 	if (j > 0)
379*4882a593Smuzhiyun 		dst[j-1] = '\n';
380*4882a593Smuzhiyun 	if (i < data_len && j > 2)
381*4882a593Smuzhiyun 		dst[j-2] = dst[j-3] = '.';
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun 
picolcd_debug_out_report(struct picolcd_data * data,struct hid_device * hdev,struct hid_report * report)384*4882a593Smuzhiyun void picolcd_debug_out_report(struct picolcd_data *data,
385*4882a593Smuzhiyun 		struct hid_device *hdev, struct hid_report *report)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	u8 *raw_data;
388*4882a593Smuzhiyun 	int raw_size = (report->size >> 3) + 1;
389*4882a593Smuzhiyun 	char *buff;
390*4882a593Smuzhiyun #define BUFF_SZ 256
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	/* Avoid unnecessary overhead if debugfs is disabled */
393*4882a593Smuzhiyun 	if (list_empty(&hdev->debug_list))
394*4882a593Smuzhiyun 		return;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
397*4882a593Smuzhiyun 	if (!buff)
398*4882a593Smuzhiyun 		return;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	raw_data = hid_alloc_report_buf(report, GFP_ATOMIC);
401*4882a593Smuzhiyun 	if (!raw_data) {
402*4882a593Smuzhiyun 		kfree(buff);
403*4882a593Smuzhiyun 		return;
404*4882a593Smuzhiyun 	}
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	snprintf(buff, BUFF_SZ, "\nout report %d (size %d) =  ",
407*4882a593Smuzhiyun 			report->id, raw_size);
408*4882a593Smuzhiyun 	hid_debug_event(hdev, buff);
409*4882a593Smuzhiyun 	raw_data[0] = report->id;
410*4882a593Smuzhiyun 	hid_output_report(report, raw_data);
411*4882a593Smuzhiyun 	dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
412*4882a593Smuzhiyun 	hid_debug_event(hdev, buff);
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	switch (report->id) {
415*4882a593Smuzhiyun 	case REPORT_LED_STATE:
416*4882a593Smuzhiyun 		/* 1 data byte with GPO state */
417*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
418*4882a593Smuzhiyun 			"REPORT_LED_STATE", report->id, raw_size-1);
419*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
420*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
421*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
422*4882a593Smuzhiyun 		break;
423*4882a593Smuzhiyun 	case REPORT_BRIGHTNESS:
424*4882a593Smuzhiyun 		/* 1 data byte with brightness */
425*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
426*4882a593Smuzhiyun 			"REPORT_BRIGHTNESS", report->id, raw_size-1);
427*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
428*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
429*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
430*4882a593Smuzhiyun 		break;
431*4882a593Smuzhiyun 	case REPORT_CONTRAST:
432*4882a593Smuzhiyun 		/* 1 data byte with contrast */
433*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
434*4882a593Smuzhiyun 			"REPORT_CONTRAST", report->id, raw_size-1);
435*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
436*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
437*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
438*4882a593Smuzhiyun 		break;
439*4882a593Smuzhiyun 	case REPORT_RESET:
440*4882a593Smuzhiyun 		/* 2 data bytes with reset duration in ms */
441*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
442*4882a593Smuzhiyun 			"REPORT_RESET", report->id, raw_size-1);
443*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
444*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
445*4882a593Smuzhiyun 				raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
446*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
447*4882a593Smuzhiyun 		break;
448*4882a593Smuzhiyun 	case REPORT_LCD_CMD:
449*4882a593Smuzhiyun 		/* 63 data bytes with LCD commands */
450*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
451*4882a593Smuzhiyun 			"REPORT_LCD_CMD", report->id, raw_size-1);
452*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
453*4882a593Smuzhiyun 		/* TODO: format decoding */
454*4882a593Smuzhiyun 		break;
455*4882a593Smuzhiyun 	case REPORT_LCD_DATA:
456*4882a593Smuzhiyun 		/* 63 data bytes with LCD data */
457*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
458*4882a593Smuzhiyun 			"REPORT_LCD_CMD", report->id, raw_size-1);
459*4882a593Smuzhiyun 		/* TODO: format decoding */
460*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
461*4882a593Smuzhiyun 		break;
462*4882a593Smuzhiyun 	case REPORT_LCD_CMD_DATA:
463*4882a593Smuzhiyun 		/* 63 data bytes with LCD commands and data */
464*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
465*4882a593Smuzhiyun 			"REPORT_LCD_CMD", report->id, raw_size-1);
466*4882a593Smuzhiyun 		/* TODO: format decoding */
467*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
468*4882a593Smuzhiyun 		break;
469*4882a593Smuzhiyun 	case REPORT_EE_READ:
470*4882a593Smuzhiyun 		/* 3 data bytes with read area description */
471*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
472*4882a593Smuzhiyun 			"REPORT_EE_READ", report->id, raw_size-1);
473*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
474*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
475*4882a593Smuzhiyun 				raw_data[2], raw_data[1]);
476*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
477*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
478*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
479*4882a593Smuzhiyun 		break;
480*4882a593Smuzhiyun 	case REPORT_EE_WRITE:
481*4882a593Smuzhiyun 		/* 3+1..20 data bytes with write area description */
482*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
483*4882a593Smuzhiyun 			"REPORT_EE_WRITE", report->id, raw_size-1);
484*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
485*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
486*4882a593Smuzhiyun 				raw_data[2], raw_data[1]);
487*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
488*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
489*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
490*4882a593Smuzhiyun 		if (raw_data[3] == 0) {
491*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNo data\n");
492*4882a593Smuzhiyun 		} else if (raw_data[3] + 4 <= raw_size) {
493*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData: ");
494*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
495*4882a593Smuzhiyun 			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
496*4882a593Smuzhiyun 		} else {
497*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
498*4882a593Smuzhiyun 		}
499*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
500*4882a593Smuzhiyun 		break;
501*4882a593Smuzhiyun 	case REPORT_ERASE_MEMORY:
502*4882a593Smuzhiyun 	case REPORT_BL_ERASE_MEMORY:
503*4882a593Smuzhiyun 		/* 3 data bytes with pointer inside erase block */
504*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
505*4882a593Smuzhiyun 			"REPORT_ERASE_MEMORY", report->id, raw_size-1);
506*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
507*4882a593Smuzhiyun 		switch (data->addr_sz) {
508*4882a593Smuzhiyun 		case 2:
509*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
510*4882a593Smuzhiyun 					raw_data[2], raw_data[1]);
511*4882a593Smuzhiyun 			break;
512*4882a593Smuzhiyun 		case 3:
513*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
514*4882a593Smuzhiyun 					raw_data[3], raw_data[2], raw_data[1]);
515*4882a593Smuzhiyun 			break;
516*4882a593Smuzhiyun 		default:
517*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNot supported\n");
518*4882a593Smuzhiyun 		}
519*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
520*4882a593Smuzhiyun 		break;
521*4882a593Smuzhiyun 	case REPORT_READ_MEMORY:
522*4882a593Smuzhiyun 	case REPORT_BL_READ_MEMORY:
523*4882a593Smuzhiyun 		/* 4 data bytes with read area description */
524*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
525*4882a593Smuzhiyun 			"REPORT_READ_MEMORY", report->id, raw_size-1);
526*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
527*4882a593Smuzhiyun 		switch (data->addr_sz) {
528*4882a593Smuzhiyun 		case 2:
529*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
530*4882a593Smuzhiyun 					raw_data[2], raw_data[1]);
531*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
532*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
533*4882a593Smuzhiyun 			break;
534*4882a593Smuzhiyun 		case 3:
535*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
536*4882a593Smuzhiyun 					raw_data[3], raw_data[2], raw_data[1]);
537*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
538*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
539*4882a593Smuzhiyun 			break;
540*4882a593Smuzhiyun 		default:
541*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNot supported\n");
542*4882a593Smuzhiyun 		}
543*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
544*4882a593Smuzhiyun 		break;
545*4882a593Smuzhiyun 	case REPORT_WRITE_MEMORY:
546*4882a593Smuzhiyun 	case REPORT_BL_WRITE_MEMORY:
547*4882a593Smuzhiyun 		/* 4+1..32 data bytes with write adrea description */
548*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
549*4882a593Smuzhiyun 			"REPORT_WRITE_MEMORY", report->id, raw_size-1);
550*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
551*4882a593Smuzhiyun 		switch (data->addr_sz) {
552*4882a593Smuzhiyun 		case 2:
553*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
554*4882a593Smuzhiyun 					raw_data[2], raw_data[1]);
555*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
556*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
557*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
558*4882a593Smuzhiyun 			if (raw_data[3] == 0) {
559*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tNo data\n");
560*4882a593Smuzhiyun 			} else if (raw_data[3] + 4 <= raw_size) {
561*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData: ");
562*4882a593Smuzhiyun 				hid_debug_event(hdev, buff);
563*4882a593Smuzhiyun 				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
564*4882a593Smuzhiyun 			} else {
565*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
566*4882a593Smuzhiyun 			}
567*4882a593Smuzhiyun 			break;
568*4882a593Smuzhiyun 		case 3:
569*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
570*4882a593Smuzhiyun 					raw_data[3], raw_data[2], raw_data[1]);
571*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
572*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
573*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
574*4882a593Smuzhiyun 			if (raw_data[4] == 0) {
575*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tNo data\n");
576*4882a593Smuzhiyun 			} else if (raw_data[4] + 5 <= raw_size) {
577*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData: ");
578*4882a593Smuzhiyun 				hid_debug_event(hdev, buff);
579*4882a593Smuzhiyun 				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
580*4882a593Smuzhiyun 			} else {
581*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
582*4882a593Smuzhiyun 			}
583*4882a593Smuzhiyun 			break;
584*4882a593Smuzhiyun 		default:
585*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNot supported\n");
586*4882a593Smuzhiyun 		}
587*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
588*4882a593Smuzhiyun 		break;
589*4882a593Smuzhiyun 	case REPORT_SPLASH_RESTART:
590*4882a593Smuzhiyun 		/* TODO */
591*4882a593Smuzhiyun 		break;
592*4882a593Smuzhiyun 	case REPORT_EXIT_KEYBOARD:
593*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
594*4882a593Smuzhiyun 			"REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
595*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
596*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
597*4882a593Smuzhiyun 				raw_data[1] | (raw_data[2] << 8),
598*4882a593Smuzhiyun 				raw_data[2], raw_data[1]);
599*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
600*4882a593Smuzhiyun 		break;
601*4882a593Smuzhiyun 	case REPORT_VERSION:
602*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
603*4882a593Smuzhiyun 			"REPORT_VERSION", report->id, raw_size-1);
604*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
605*4882a593Smuzhiyun 		break;
606*4882a593Smuzhiyun 	case REPORT_DEVID:
607*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
608*4882a593Smuzhiyun 			"REPORT_DEVID", report->id, raw_size-1);
609*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
610*4882a593Smuzhiyun 		break;
611*4882a593Smuzhiyun 	case REPORT_SPLASH_SIZE:
612*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
613*4882a593Smuzhiyun 			"REPORT_SPLASH_SIZE", report->id, raw_size-1);
614*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
615*4882a593Smuzhiyun 		break;
616*4882a593Smuzhiyun 	case REPORT_HOOK_VERSION:
617*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
618*4882a593Smuzhiyun 			"REPORT_HOOK_VERSION", report->id, raw_size-1);
619*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
620*4882a593Smuzhiyun 		break;
621*4882a593Smuzhiyun 	case REPORT_EXIT_FLASHER:
622*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
623*4882a593Smuzhiyun 			"REPORT_VERSION", report->id, raw_size-1);
624*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
625*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
626*4882a593Smuzhiyun 				raw_data[1] | (raw_data[2] << 8),
627*4882a593Smuzhiyun 				raw_data[2], raw_data[1]);
628*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
629*4882a593Smuzhiyun 		break;
630*4882a593Smuzhiyun 	default:
631*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
632*4882a593Smuzhiyun 			"<unknown>", report->id, raw_size-1);
633*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
634*4882a593Smuzhiyun 		break;
635*4882a593Smuzhiyun 	}
636*4882a593Smuzhiyun 	wake_up_interruptible(&hdev->debug_wait);
637*4882a593Smuzhiyun 	kfree(raw_data);
638*4882a593Smuzhiyun 	kfree(buff);
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun 
picolcd_debug_raw_event(struct picolcd_data * data,struct hid_device * hdev,struct hid_report * report,u8 * raw_data,int size)641*4882a593Smuzhiyun void picolcd_debug_raw_event(struct picolcd_data *data,
642*4882a593Smuzhiyun 		struct hid_device *hdev, struct hid_report *report,
643*4882a593Smuzhiyun 		u8 *raw_data, int size)
644*4882a593Smuzhiyun {
645*4882a593Smuzhiyun 	char *buff;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun #define BUFF_SZ 256
648*4882a593Smuzhiyun 	/* Avoid unnecessary overhead if debugfs is disabled */
649*4882a593Smuzhiyun 	if (list_empty(&hdev->debug_list))
650*4882a593Smuzhiyun 		return;
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
653*4882a593Smuzhiyun 	if (!buff)
654*4882a593Smuzhiyun 		return;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	switch (report->id) {
657*4882a593Smuzhiyun 	case REPORT_ERROR_CODE:
658*4882a593Smuzhiyun 		/* 2 data bytes with affected report and error code */
659*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
660*4882a593Smuzhiyun 			"REPORT_ERROR_CODE", report->id, size-1);
661*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
662*4882a593Smuzhiyun 		if (raw_data[2] < ARRAY_SIZE(error_codes))
663*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
664*4882a593Smuzhiyun 					raw_data[2], error_codes[raw_data[2]], raw_data[1]);
665*4882a593Smuzhiyun 		else
666*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
667*4882a593Smuzhiyun 					raw_data[2], raw_data[1]);
668*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
669*4882a593Smuzhiyun 		break;
670*4882a593Smuzhiyun 	case REPORT_KEY_STATE:
671*4882a593Smuzhiyun 		/* 2 data bytes with key state */
672*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
673*4882a593Smuzhiyun 			"REPORT_KEY_STATE", report->id, size-1);
674*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
675*4882a593Smuzhiyun 		if (raw_data[1] == 0)
676*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
677*4882a593Smuzhiyun 		else if (raw_data[2] == 0)
678*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
679*4882a593Smuzhiyun 					raw_data[1], raw_data[1]);
680*4882a593Smuzhiyun 		else
681*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
682*4882a593Smuzhiyun 					raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
683*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
684*4882a593Smuzhiyun 		break;
685*4882a593Smuzhiyun 	case REPORT_IR_DATA:
686*4882a593Smuzhiyun 		/* Up to 20 byes of IR scancode data */
687*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
688*4882a593Smuzhiyun 			"REPORT_IR_DATA", report->id, size-1);
689*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
690*4882a593Smuzhiyun 		if (raw_data[1] == 0) {
691*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
692*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
693*4882a593Smuzhiyun 		} else if (raw_data[1] + 1 <= size) {
694*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
695*4882a593Smuzhiyun 					raw_data[1]);
696*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
697*4882a593Smuzhiyun 			dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
698*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
699*4882a593Smuzhiyun 		} else {
700*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
701*4882a593Smuzhiyun 					raw_data[1]-1);
702*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
703*4882a593Smuzhiyun 		}
704*4882a593Smuzhiyun 		break;
705*4882a593Smuzhiyun 	case REPORT_EE_DATA:
706*4882a593Smuzhiyun 		/* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
707*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
708*4882a593Smuzhiyun 			"REPORT_EE_DATA", report->id, size-1);
709*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
710*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
711*4882a593Smuzhiyun 				raw_data[2], raw_data[1]);
712*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
713*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
714*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
715*4882a593Smuzhiyun 		if (raw_data[3] == 0) {
716*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNo data\n");
717*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
718*4882a593Smuzhiyun 		} else if (raw_data[3] + 4 <= size) {
719*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData: ");
720*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
721*4882a593Smuzhiyun 			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
722*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
723*4882a593Smuzhiyun 		} else {
724*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
725*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
726*4882a593Smuzhiyun 		}
727*4882a593Smuzhiyun 		break;
728*4882a593Smuzhiyun 	case REPORT_MEMORY:
729*4882a593Smuzhiyun 		/* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRITE_MEMORY */
730*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
731*4882a593Smuzhiyun 			"REPORT_MEMORY", report->id, size-1);
732*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
733*4882a593Smuzhiyun 		switch (data->addr_sz) {
734*4882a593Smuzhiyun 		case 2:
735*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
736*4882a593Smuzhiyun 					raw_data[2], raw_data[1]);
737*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
738*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
739*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
740*4882a593Smuzhiyun 			if (raw_data[3] == 0) {
741*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tNo data\n");
742*4882a593Smuzhiyun 			} else if (raw_data[3] + 4 <= size) {
743*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData: ");
744*4882a593Smuzhiyun 				hid_debug_event(hdev, buff);
745*4882a593Smuzhiyun 				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
746*4882a593Smuzhiyun 			} else {
747*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
748*4882a593Smuzhiyun 			}
749*4882a593Smuzhiyun 			break;
750*4882a593Smuzhiyun 		case 3:
751*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
752*4882a593Smuzhiyun 					raw_data[3], raw_data[2], raw_data[1]);
753*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
754*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
755*4882a593Smuzhiyun 			hid_debug_event(hdev, buff);
756*4882a593Smuzhiyun 			if (raw_data[4] == 0) {
757*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tNo data\n");
758*4882a593Smuzhiyun 			} else if (raw_data[4] + 5 <= size) {
759*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData: ");
760*4882a593Smuzhiyun 				hid_debug_event(hdev, buff);
761*4882a593Smuzhiyun 				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
762*4882a593Smuzhiyun 			} else {
763*4882a593Smuzhiyun 				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
764*4882a593Smuzhiyun 			}
765*4882a593Smuzhiyun 			break;
766*4882a593Smuzhiyun 		default:
767*4882a593Smuzhiyun 			snprintf(buff, BUFF_SZ, "\tNot supported\n");
768*4882a593Smuzhiyun 		}
769*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
770*4882a593Smuzhiyun 		break;
771*4882a593Smuzhiyun 	case REPORT_VERSION:
772*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
773*4882a593Smuzhiyun 			"REPORT_VERSION", report->id, size-1);
774*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
775*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
776*4882a593Smuzhiyun 				raw_data[2], raw_data[1]);
777*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
778*4882a593Smuzhiyun 		break;
779*4882a593Smuzhiyun 	case REPORT_BL_ERASE_MEMORY:
780*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
781*4882a593Smuzhiyun 			"REPORT_BL_ERASE_MEMORY", report->id, size-1);
782*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
783*4882a593Smuzhiyun 		/* TODO */
784*4882a593Smuzhiyun 		break;
785*4882a593Smuzhiyun 	case REPORT_BL_READ_MEMORY:
786*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
787*4882a593Smuzhiyun 			"REPORT_BL_READ_MEMORY", report->id, size-1);
788*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
789*4882a593Smuzhiyun 		/* TODO */
790*4882a593Smuzhiyun 		break;
791*4882a593Smuzhiyun 	case REPORT_BL_WRITE_MEMORY:
792*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
793*4882a593Smuzhiyun 			"REPORT_BL_WRITE_MEMORY", report->id, size-1);
794*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
795*4882a593Smuzhiyun 		/* TODO */
796*4882a593Smuzhiyun 		break;
797*4882a593Smuzhiyun 	case REPORT_DEVID:
798*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
799*4882a593Smuzhiyun 			"REPORT_DEVID", report->id, size-1);
800*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
801*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
802*4882a593Smuzhiyun 				raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
803*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
804*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
805*4882a593Smuzhiyun 				raw_data[5]);
806*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
807*4882a593Smuzhiyun 		break;
808*4882a593Smuzhiyun 	case REPORT_SPLASH_SIZE:
809*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
810*4882a593Smuzhiyun 			"REPORT_SPLASH_SIZE", report->id, size-1);
811*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
812*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
813*4882a593Smuzhiyun 				(raw_data[2] << 8) | raw_data[1]);
814*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
815*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
816*4882a593Smuzhiyun 				(raw_data[4] << 8) | raw_data[3]);
817*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
818*4882a593Smuzhiyun 		break;
819*4882a593Smuzhiyun 	case REPORT_HOOK_VERSION:
820*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
821*4882a593Smuzhiyun 			"REPORT_HOOK_VERSION", report->id, size-1);
822*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
823*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
824*4882a593Smuzhiyun 				raw_data[1], raw_data[2]);
825*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
826*4882a593Smuzhiyun 		break;
827*4882a593Smuzhiyun 	default:
828*4882a593Smuzhiyun 		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
829*4882a593Smuzhiyun 			"<unknown>", report->id, size-1);
830*4882a593Smuzhiyun 		hid_debug_event(hdev, buff);
831*4882a593Smuzhiyun 		break;
832*4882a593Smuzhiyun 	}
833*4882a593Smuzhiyun 	wake_up_interruptible(&hdev->debug_wait);
834*4882a593Smuzhiyun 	kfree(buff);
835*4882a593Smuzhiyun }
836*4882a593Smuzhiyun 
picolcd_init_devfs(struct picolcd_data * data,struct hid_report * eeprom_r,struct hid_report * eeprom_w,struct hid_report * flash_r,struct hid_report * flash_w,struct hid_report * reset)837*4882a593Smuzhiyun void picolcd_init_devfs(struct picolcd_data *data,
838*4882a593Smuzhiyun 		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
839*4882a593Smuzhiyun 		struct hid_report *flash_r, struct hid_report *flash_w,
840*4882a593Smuzhiyun 		struct hid_report *reset)
841*4882a593Smuzhiyun {
842*4882a593Smuzhiyun 	struct hid_device *hdev = data->hdev;
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	mutex_init(&data->mutex_flash);
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 	/* reset */
847*4882a593Smuzhiyun 	if (reset)
848*4882a593Smuzhiyun 		data->debug_reset = debugfs_create_file("reset", 0600,
849*4882a593Smuzhiyun 				hdev->debug_dir, data, &picolcd_debug_reset_fops);
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	/* eeprom */
852*4882a593Smuzhiyun 	if (eeprom_r || eeprom_w)
853*4882a593Smuzhiyun 		data->debug_eeprom = debugfs_create_file("eeprom",
854*4882a593Smuzhiyun 			(eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
855*4882a593Smuzhiyun 			hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	/* flash */
858*4882a593Smuzhiyun 	if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
859*4882a593Smuzhiyun 		data->addr_sz = flash_r->field[0]->report_count - 1;
860*4882a593Smuzhiyun 	else
861*4882a593Smuzhiyun 		data->addr_sz = -1;
862*4882a593Smuzhiyun 	if (data->addr_sz == 2 || data->addr_sz == 3) {
863*4882a593Smuzhiyun 		data->debug_flash = debugfs_create_file("flash",
864*4882a593Smuzhiyun 			(flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
865*4882a593Smuzhiyun 			hdev->debug_dir, data, &picolcd_debug_flash_fops);
866*4882a593Smuzhiyun 	} else if (flash_r || flash_w)
867*4882a593Smuzhiyun 		hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun 
picolcd_exit_devfs(struct picolcd_data * data)870*4882a593Smuzhiyun void picolcd_exit_devfs(struct picolcd_data *data)
871*4882a593Smuzhiyun {
872*4882a593Smuzhiyun 	struct dentry *dent;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	dent = data->debug_reset;
875*4882a593Smuzhiyun 	data->debug_reset = NULL;
876*4882a593Smuzhiyun 	debugfs_remove(dent);
877*4882a593Smuzhiyun 	dent = data->debug_eeprom;
878*4882a593Smuzhiyun 	data->debug_eeprom = NULL;
879*4882a593Smuzhiyun 	debugfs_remove(dent);
880*4882a593Smuzhiyun 	dent = data->debug_flash;
881*4882a593Smuzhiyun 	data->debug_flash = NULL;
882*4882a593Smuzhiyun 	debugfs_remove(dent);
883*4882a593Smuzhiyun 	mutex_destroy(&data->mutex_flash);
884*4882a593Smuzhiyun }
885*4882a593Smuzhiyun 
886