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