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