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