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