diff options
author | Bruno Prémont <bonbons@linux-vserver.org> | 2010-03-30 16:33:50 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-03-31 05:20:59 -0400 |
commit | 236db47c2b3b69464d50c695ab2ddd516cf64520 (patch) | |
tree | 599125d28f2f0a23be2da9dc59d783fc404b65b0 /drivers/hid/hid-picolcd.c | |
parent | 39710479303fd3affb3e204e9a7a75cc676977b5 (diff) |
HID: new driver for PicoLCD device
Add basic driver for PicoLCD graphics device.
Initially support keypad with input device and provide support
for debugging communication via events file from debugfs.
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.c')
-rw-r--r-- | drivers/hid/hid-picolcd.c | 1183 |
1 files changed, 1183 insertions, 0 deletions
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c new file mode 100644 index 000000000000..c7855f388898 --- /dev/null +++ b/drivers/hid/hid-picolcd.c | |||
@@ -0,0 +1,1183 @@ | |||
1 | /*************************************************************************** | ||
2 | * Copyright (C) 2010 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 <linux/input.h> | ||
23 | #include "hid-ids.h" | ||
24 | #include "usbhid/usbhid.h" | ||
25 | #include <linux/usb.h> | ||
26 | |||
27 | #include <linux/seq_file.h> | ||
28 | #include <linux/debugfs.h> | ||
29 | |||
30 | #include <linux/completion.h> | ||
31 | |||
32 | #define PICOLCD_NAME "PicoLCD (graphic)" | ||
33 | |||
34 | /* Report numbers */ | ||
35 | #define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */ | ||
36 | #define ERR_SUCCESS 0x00 | ||
37 | #define ERR_PARAMETER_MISSING 0x01 | ||
38 | #define ERR_DATA_MISSING 0x02 | ||
39 | #define ERR_BLOCK_READ_ONLY 0x03 | ||
40 | #define ERR_BLOCK_NOT_ERASABLE 0x04 | ||
41 | #define ERR_BLOCK_TOO_BIG 0x05 | ||
42 | #define ERR_SECTION_OVERFLOW 0x06 | ||
43 | #define ERR_INVALID_CMD_LEN 0x07 | ||
44 | #define ERR_INVALID_DATA_LEN 0x08 | ||
45 | #define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */ | ||
46 | #define REPORT_IR_DATA 0x21 /* LCD: IN[63] */ | ||
47 | #define REPORT_EE_DATA 0x32 /* LCD: IN[63] */ | ||
48 | #define REPORT_MEMORY 0x41 /* LCD: IN[63] */ | ||
49 | #define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */ | ||
50 | #define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */ | ||
51 | #define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */ | ||
52 | #define REPORT_RESET 0x93 /* LCD: OUT[2] */ | ||
53 | #define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */ | ||
54 | #define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */ | ||
55 | #define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */ | ||
56 | #define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */ | ||
57 | #define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */ | ||
58 | #define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */ | ||
59 | #define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */ | ||
60 | #define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */ | ||
61 | #define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */ | ||
62 | #define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */ | ||
63 | #define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */ | ||
64 | #define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */ | ||
65 | #define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */ | ||
66 | #define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */ | ||
67 | #define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */ | ||
68 | #define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */ | ||
69 | #define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */ | ||
70 | #define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */ | ||
71 | |||
72 | /* Input device | ||
73 | * | ||
74 | * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys | ||
75 | * and header for 4x4 key matrix. The built-in keys are part of the matrix. | ||
76 | */ | ||
77 | static const unsigned short def_keymap[] = { | ||
78 | KEY_RESERVED, /* none */ | ||
79 | KEY_BACK, /* col 4 + row 1 */ | ||
80 | KEY_HOMEPAGE, /* col 3 + row 1 */ | ||
81 | KEY_RESERVED, /* col 2 + row 1 */ | ||
82 | KEY_RESERVED, /* col 1 + row 1 */ | ||
83 | KEY_SCROLLUP, /* col 4 + row 2 */ | ||
84 | KEY_OK, /* col 3 + row 2 */ | ||
85 | KEY_SCROLLDOWN, /* col 2 + row 2 */ | ||
86 | KEY_RESERVED, /* col 1 + row 2 */ | ||
87 | KEY_RESERVED, /* col 4 + row 3 */ | ||
88 | KEY_RESERVED, /* col 3 + row 3 */ | ||
89 | KEY_RESERVED, /* col 2 + row 3 */ | ||
90 | KEY_RESERVED, /* col 1 + row 3 */ | ||
91 | KEY_RESERVED, /* col 4 + row 4 */ | ||
92 | KEY_RESERVED, /* col 3 + row 4 */ | ||
93 | KEY_RESERVED, /* col 2 + row 4 */ | ||
94 | KEY_RESERVED, /* col 1 + row 4 */ | ||
95 | }; | ||
96 | #define PICOLCD_KEYS ARRAY_SIZE(def_keymap) | ||
97 | |||
98 | /* Description of in-progress IO operation, used for operations | ||
99 | * that trigger response from device */ | ||
100 | struct picolcd_pending { | ||
101 | struct hid_report *out_report; | ||
102 | struct hid_report *in_report; | ||
103 | struct completion ready; | ||
104 | int raw_size; | ||
105 | u8 raw_data[64]; | ||
106 | }; | ||
107 | |||
108 | /* Per device data structure */ | ||
109 | struct picolcd_data { | ||
110 | struct hid_device *hdev; | ||
111 | #ifdef CONFIG_DEBUG_FS | ||
112 | int addr_sz; | ||
113 | #endif | ||
114 | u8 version[2]; | ||
115 | /* input stuff */ | ||
116 | u8 pressed_keys[2]; | ||
117 | struct input_dev *input_keys; | ||
118 | struct input_dev *input_cir; | ||
119 | unsigned short keycode[PICOLCD_KEYS]; | ||
120 | |||
121 | /* Housekeeping stuff */ | ||
122 | spinlock_t lock; | ||
123 | struct mutex mutex; | ||
124 | struct picolcd_pending *pending; | ||
125 | int status; | ||
126 | #define PICOLCD_BOOTLOADER 1 | ||
127 | #define PICOLCD_FAILED 2 | ||
128 | }; | ||
129 | |||
130 | |||
131 | /* Find a given report */ | ||
132 | #define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT) | ||
133 | #define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT) | ||
134 | |||
135 | static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir) | ||
136 | { | ||
137 | struct list_head *feature_report_list = &hdev->report_enum[dir].report_list; | ||
138 | struct hid_report *report = NULL; | ||
139 | |||
140 | list_for_each_entry(report, feature_report_list, list) { | ||
141 | if (report->id == id) | ||
142 | return report; | ||
143 | } | ||
144 | dev_warn(&hdev->dev, "No report with id 0x%x found\n", id); | ||
145 | return NULL; | ||
146 | } | ||
147 | |||
148 | #ifdef CONFIG_DEBUG_FS | ||
149 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
150 | struct hid_device *hdev, struct hid_report *report); | ||
151 | #define usbhid_submit_report(a, b, c) \ | ||
152 | do { \ | ||
153 | picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ | ||
154 | usbhid_submit_report(a, b, c); \ | ||
155 | } while (0) | ||
156 | #endif | ||
157 | |||
158 | /* Submit a report and wait for a reply from device - if device fades away | ||
159 | * or does not respond in time, return NULL */ | ||
160 | static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | ||
161 | int report_id, const u8 *raw_data, int size) | ||
162 | { | ||
163 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
164 | struct picolcd_pending *work; | ||
165 | struct hid_report *report = picolcd_out_report(report_id, hdev); | ||
166 | unsigned long flags; | ||
167 | int i, j, k; | ||
168 | |||
169 | if (!report || !data) | ||
170 | return NULL; | ||
171 | if (data->status & PICOLCD_FAILED) | ||
172 | return NULL; | ||
173 | work = kzalloc(sizeof(*work), GFP_KERNEL); | ||
174 | if (!work) | ||
175 | return NULL; | ||
176 | |||
177 | init_completion(&work->ready); | ||
178 | work->out_report = report; | ||
179 | work->in_report = NULL; | ||
180 | work->raw_size = 0; | ||
181 | |||
182 | mutex_lock(&data->mutex); | ||
183 | spin_lock_irqsave(&data->lock, flags); | ||
184 | for (i = k = 0; i < report->maxfield; i++) | ||
185 | for (j = 0; j < report->field[i]->report_count; j++) { | ||
186 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); | ||
187 | k++; | ||
188 | } | ||
189 | data->pending = work; | ||
190 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
191 | spin_unlock_irqrestore(&data->lock, flags); | ||
192 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | ||
193 | spin_lock_irqsave(&data->lock, flags); | ||
194 | data->pending = NULL; | ||
195 | spin_unlock_irqrestore(&data->lock, flags); | ||
196 | mutex_unlock(&data->mutex); | ||
197 | return work; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * input class device | ||
202 | */ | ||
203 | static int picolcd_raw_keypad(struct picolcd_data *data, | ||
204 | struct hid_report *report, u8 *raw_data, int size) | ||
205 | { | ||
206 | /* | ||
207 | * Keypad event | ||
208 | * First and second data bytes list currently pressed keys, | ||
209 | * 0x00 means no key and at most 2 keys may be pressed at same time | ||
210 | */ | ||
211 | int i, j; | ||
212 | |||
213 | /* determine newly pressed keys */ | ||
214 | for (i = 0; i < size; i++) { | ||
215 | unsigned int key_code; | ||
216 | if (raw_data[i] == 0) | ||
217 | continue; | ||
218 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
219 | if (data->pressed_keys[j] == raw_data[i]) | ||
220 | goto key_already_down; | ||
221 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
222 | if (data->pressed_keys[j] == 0) { | ||
223 | data->pressed_keys[j] = raw_data[i]; | ||
224 | break; | ||
225 | } | ||
226 | input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]); | ||
227 | if (raw_data[i] < PICOLCD_KEYS) | ||
228 | key_code = data->keycode[raw_data[i]]; | ||
229 | else | ||
230 | key_code = KEY_UNKNOWN; | ||
231 | if (key_code != KEY_UNKNOWN) { | ||
232 | dbg_hid(PICOLCD_NAME " got key press for %u:%d", | ||
233 | raw_data[i], key_code); | ||
234 | input_report_key(data->input_keys, key_code, 1); | ||
235 | } | ||
236 | input_sync(data->input_keys); | ||
237 | key_already_down: | ||
238 | continue; | ||
239 | } | ||
240 | |||
241 | /* determine newly released keys */ | ||
242 | for (j = 0; j < sizeof(data->pressed_keys); j++) { | ||
243 | unsigned int key_code; | ||
244 | if (data->pressed_keys[j] == 0) | ||
245 | continue; | ||
246 | for (i = 0; i < size; i++) | ||
247 | if (data->pressed_keys[j] == raw_data[i]) | ||
248 | goto key_still_down; | ||
249 | input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]); | ||
250 | if (data->pressed_keys[j] < PICOLCD_KEYS) | ||
251 | key_code = data->keycode[data->pressed_keys[j]]; | ||
252 | else | ||
253 | key_code = KEY_UNKNOWN; | ||
254 | if (key_code != KEY_UNKNOWN) { | ||
255 | dbg_hid(PICOLCD_NAME " got key release for %u:%d", | ||
256 | data->pressed_keys[j], key_code); | ||
257 | input_report_key(data->input_keys, key_code, 0); | ||
258 | } | ||
259 | input_sync(data->input_keys); | ||
260 | data->pressed_keys[j] = 0; | ||
261 | key_still_down: | ||
262 | continue; | ||
263 | } | ||
264 | return 1; | ||
265 | } | ||
266 | |||
267 | static int picolcd_raw_cir(struct picolcd_data *data, | ||
268 | struct hid_report *report, u8 *raw_data, int size) | ||
269 | { | ||
270 | /* Need understanding of CIR data format to implement ... */ | ||
271 | return 1; | ||
272 | } | ||
273 | |||
274 | static int picolcd_check_version(struct hid_device *hdev) | ||
275 | { | ||
276 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
277 | struct picolcd_pending *verinfo; | ||
278 | int ret = 0; | ||
279 | |||
280 | if (!data) | ||
281 | return -ENODEV; | ||
282 | |||
283 | verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0); | ||
284 | if (!verinfo) { | ||
285 | dev_err(&hdev->dev, "no version response from PicoLCD"); | ||
286 | return -ENODEV; | ||
287 | } | ||
288 | |||
289 | if (verinfo->raw_size == 2) { | ||
290 | if (data->status & PICOLCD_BOOTLOADER) { | ||
291 | dev_info(&hdev->dev, "PicoLCD, bootloader version %d.%d\n", | ||
292 | verinfo->raw_data[0], verinfo->raw_data[1]); | ||
293 | data->version[0] = verinfo->raw_data[0]; | ||
294 | data->version[1] = verinfo->raw_data[1]; | ||
295 | } else { | ||
296 | dev_info(&hdev->dev, "PicoLCD, firmware version %d.%d\n", | ||
297 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
298 | data->version[0] = verinfo->raw_data[1]; | ||
299 | data->version[1] = verinfo->raw_data[0]; | ||
300 | } | ||
301 | } else { | ||
302 | dev_err(&hdev->dev, "confused, got unexpected version response from PicoLCD\n"); | ||
303 | ret = -EINVAL; | ||
304 | } | ||
305 | kfree(verinfo); | ||
306 | return ret; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Reset our device and wait for answer to VERSION request | ||
311 | */ | ||
312 | static int picolcd_reset(struct hid_device *hdev) | ||
313 | { | ||
314 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
315 | struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); | ||
316 | unsigned long flags; | ||
317 | |||
318 | if (!data || !report || report->maxfield != 1) | ||
319 | return -ENODEV; | ||
320 | |||
321 | spin_lock_irqsave(&data->lock, flags); | ||
322 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
323 | data->status |= PICOLCD_BOOTLOADER; | ||
324 | |||
325 | /* perform the reset */ | ||
326 | hid_set_field(report->field[0], 0, 1); | ||
327 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
328 | spin_unlock_irqrestore(&data->lock, flags); | ||
329 | |||
330 | return picolcd_check_version(hdev); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * The "operation_mode" sysfs attribute | ||
335 | */ | ||
336 | static ssize_t picolcd_operation_mode_show(struct device *dev, | ||
337 | struct device_attribute *attr, char *buf) | ||
338 | { | ||
339 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
340 | |||
341 | if (data->status & PICOLCD_BOOTLOADER) | ||
342 | return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n"); | ||
343 | else | ||
344 | return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n"); | ||
345 | } | ||
346 | |||
347 | static ssize_t picolcd_operation_mode_store(struct device *dev, | ||
348 | struct device_attribute *attr, const char *buf, size_t count) | ||
349 | { | ||
350 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
351 | struct hid_report *report = NULL; | ||
352 | size_t cnt = count; | ||
353 | int timeout = 5000; | ||
354 | unsigned u; | ||
355 | unsigned long flags; | ||
356 | |||
357 | if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) { | ||
358 | if (data->status & PICOLCD_BOOTLOADER) | ||
359 | report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev); | ||
360 | buf += 3; | ||
361 | cnt -= 3; | ||
362 | } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) { | ||
363 | if (!(data->status & PICOLCD_BOOTLOADER)) | ||
364 | report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev); | ||
365 | buf += 10; | ||
366 | cnt -= 10; | ||
367 | } | ||
368 | if (!report) | ||
369 | return -EINVAL; | ||
370 | |||
371 | while (cnt > 0 && (*buf == ' ' || *buf == '\t')) { | ||
372 | buf++; | ||
373 | cnt--; | ||
374 | } | ||
375 | while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) | ||
376 | cnt--; | ||
377 | if (cnt > 0) { | ||
378 | if (sscanf(buf, "%u", &u) != 1) | ||
379 | return -EINVAL; | ||
380 | if (u > 30000) | ||
381 | return -EINVAL; | ||
382 | else | ||
383 | timeout = u; | ||
384 | } | ||
385 | |||
386 | spin_lock_irqsave(&data->lock, flags); | ||
387 | hid_set_field(report->field[0], 0, timeout & 0xff); | ||
388 | hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); | ||
389 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
390 | spin_unlock_irqrestore(&data->lock, flags); | ||
391 | return count; | ||
392 | } | ||
393 | |||
394 | static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show, | ||
395 | picolcd_operation_mode_store); | ||
396 | |||
397 | |||
398 | #ifdef CONFIG_DEBUG_FS | ||
399 | /* | ||
400 | * Helper code for HID report level dumping/debugging | ||
401 | */ | ||
402 | static const char *error_codes[] = { | ||
403 | "success", "parameter missing", "data_missing", "block readonly", | ||
404 | "block not erasable", "block too big", "section overflow", | ||
405 | "invalid command length", "invalid data length", | ||
406 | }; | ||
407 | |||
408 | static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, | ||
409 | const size_t data_len) | ||
410 | { | ||
411 | int i, j; | ||
412 | for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) { | ||
413 | dst[j++] = hex_asc[(data[i] >> 4) & 0x0f]; | ||
414 | dst[j++] = hex_asc[data[i] & 0x0f]; | ||
415 | dst[j++] = ' '; | ||
416 | } | ||
417 | if (j < dst_sz) { | ||
418 | dst[j--] = '\0'; | ||
419 | dst[j] = '\n'; | ||
420 | } else | ||
421 | dst[j] = '\0'; | ||
422 | } | ||
423 | |||
424 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
425 | struct hid_device *hdev, struct hid_report *report) | ||
426 | { | ||
427 | u8 raw_data[70]; | ||
428 | int raw_size = (report->size >> 3) + 1; | ||
429 | char *buff; | ||
430 | #define BUFF_SZ 256 | ||
431 | |||
432 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
433 | if (!hdev->debug_events) | ||
434 | return; | ||
435 | |||
436 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
437 | if (!buff) | ||
438 | return; | ||
439 | |||
440 | snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", | ||
441 | report->id, raw_size); | ||
442 | hid_debug_event(hdev, buff); | ||
443 | if (raw_size + 5 > sizeof(raw_data)) { | ||
444 | hid_debug_event(hdev, " TOO BIG\n"); | ||
445 | return; | ||
446 | } else { | ||
447 | raw_data[0] = report->id; | ||
448 | hid_output_report(report, raw_data); | ||
449 | dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); | ||
450 | hid_debug_event(hdev, buff); | ||
451 | } | ||
452 | |||
453 | switch (report->id) { | ||
454 | case REPORT_LED_STATE: | ||
455 | /* 1 data byte with GPO state */ | ||
456 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
457 | "REPORT_LED_STATE", report->id, raw_size-1); | ||
458 | hid_debug_event(hdev, buff); | ||
459 | snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]); | ||
460 | hid_debug_event(hdev, buff); | ||
461 | break; | ||
462 | case REPORT_BRIGHTNESS: | ||
463 | /* 1 data byte with brightness */ | ||
464 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
465 | "REPORT_BRIGHTNESS", report->id, raw_size-1); | ||
466 | hid_debug_event(hdev, buff); | ||
467 | snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]); | ||
468 | hid_debug_event(hdev, buff); | ||
469 | break; | ||
470 | case REPORT_CONTRAST: | ||
471 | /* 1 data byte with contrast */ | ||
472 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
473 | "REPORT_CONTRAST", report->id, raw_size-1); | ||
474 | hid_debug_event(hdev, buff); | ||
475 | snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]); | ||
476 | hid_debug_event(hdev, buff); | ||
477 | break; | ||
478 | case REPORT_RESET: | ||
479 | /* 2 data bytes with reset duration in ms */ | ||
480 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
481 | "REPORT_RESET", report->id, raw_size-1); | ||
482 | hid_debug_event(hdev, buff); | ||
483 | snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n", | ||
484 | raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]); | ||
485 | hid_debug_event(hdev, buff); | ||
486 | break; | ||
487 | case REPORT_LCD_CMD: | ||
488 | /* 63 data bytes with LCD commands */ | ||
489 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
490 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
491 | hid_debug_event(hdev, buff); | ||
492 | /* TODO: format decoding */ | ||
493 | break; | ||
494 | case REPORT_LCD_DATA: | ||
495 | /* 63 data bytes with LCD data */ | ||
496 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
497 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
498 | /* TODO: format decoding */ | ||
499 | hid_debug_event(hdev, buff); | ||
500 | break; | ||
501 | case REPORT_LCD_CMD_DATA: | ||
502 | /* 63 data bytes with LCD commands and data */ | ||
503 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
504 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
505 | /* TODO: format decoding */ | ||
506 | hid_debug_event(hdev, buff); | ||
507 | break; | ||
508 | case REPORT_EE_READ: | ||
509 | /* 3 data bytes with read area description */ | ||
510 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
511 | "REPORT_EE_READ", report->id, raw_size-1); | ||
512 | hid_debug_event(hdev, buff); | ||
513 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
514 | raw_data[2], raw_data[1]); | ||
515 | hid_debug_event(hdev, buff); | ||
516 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
517 | hid_debug_event(hdev, buff); | ||
518 | break; | ||
519 | case REPORT_EE_WRITE: | ||
520 | /* 3+1..20 data bytes with write area description */ | ||
521 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
522 | "REPORT_EE_WRITE", report->id, raw_size-1); | ||
523 | hid_debug_event(hdev, buff); | ||
524 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
525 | raw_data[2], raw_data[1]); | ||
526 | hid_debug_event(hdev, buff); | ||
527 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
528 | hid_debug_event(hdev, buff); | ||
529 | if (raw_data[3] == 0) { | ||
530 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
531 | } else if (raw_data[3] + 4 <= raw_size) { | ||
532 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
533 | hid_debug_event(hdev, buff); | ||
534 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
535 | } else { | ||
536 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
537 | } | ||
538 | hid_debug_event(hdev, buff); | ||
539 | break; | ||
540 | case REPORT_ERASE_MEMORY: | ||
541 | case REPORT_BL_ERASE_MEMORY: | ||
542 | /* 3 data bytes with pointer inside erase block */ | ||
543 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
544 | "REPORT_ERASE_MEMORY", report->id, raw_size-1); | ||
545 | hid_debug_event(hdev, buff); | ||
546 | switch (data->addr_sz) { | ||
547 | case 2: | ||
548 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n", | ||
549 | raw_data[2], raw_data[1]); | ||
550 | break; | ||
551 | case 3: | ||
552 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n", | ||
553 | raw_data[3], raw_data[2], raw_data[1]); | ||
554 | break; | ||
555 | default: | ||
556 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
557 | } | ||
558 | hid_debug_event(hdev, buff); | ||
559 | break; | ||
560 | case REPORT_READ_MEMORY: | ||
561 | case REPORT_BL_READ_MEMORY: | ||
562 | /* 4 data bytes with read area description */ | ||
563 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
564 | "REPORT_READ_MEMORY", report->id, raw_size-1); | ||
565 | hid_debug_event(hdev, buff); | ||
566 | switch (data->addr_sz) { | ||
567 | case 2: | ||
568 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
569 | raw_data[2], raw_data[1]); | ||
570 | hid_debug_event(hdev, buff); | ||
571 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
572 | break; | ||
573 | case 3: | ||
574 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
575 | raw_data[3], raw_data[2], raw_data[1]); | ||
576 | hid_debug_event(hdev, buff); | ||
577 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
578 | break; | ||
579 | default: | ||
580 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
581 | } | ||
582 | hid_debug_event(hdev, buff); | ||
583 | break; | ||
584 | case REPORT_WRITE_MEMORY: | ||
585 | case REPORT_BL_WRITE_MEMORY: | ||
586 | /* 4+1..32 data bytes with write adrea description */ | ||
587 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
588 | "REPORT_WRITE_MEMORY", report->id, raw_size-1); | ||
589 | hid_debug_event(hdev, buff); | ||
590 | switch (data->addr_sz) { | ||
591 | case 2: | ||
592 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
593 | raw_data[2], raw_data[1]); | ||
594 | hid_debug_event(hdev, buff); | ||
595 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
596 | hid_debug_event(hdev, buff); | ||
597 | if (raw_data[3] == 0) { | ||
598 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
599 | } else if (raw_data[3] + 4 <= raw_size) { | ||
600 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
601 | hid_debug_event(hdev, buff); | ||
602 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
603 | } else { | ||
604 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
605 | } | ||
606 | break; | ||
607 | case 3: | ||
608 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
609 | raw_data[3], raw_data[2], raw_data[1]); | ||
610 | hid_debug_event(hdev, buff); | ||
611 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
612 | hid_debug_event(hdev, buff); | ||
613 | if (raw_data[4] == 0) { | ||
614 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
615 | } else if (raw_data[4] + 5 <= raw_size) { | ||
616 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
617 | hid_debug_event(hdev, buff); | ||
618 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
619 | } else { | ||
620 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
621 | } | ||
622 | break; | ||
623 | default: | ||
624 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
625 | } | ||
626 | hid_debug_event(hdev, buff); | ||
627 | break; | ||
628 | case REPORT_SPLASH_RESTART: | ||
629 | /* TODO */ | ||
630 | break; | ||
631 | case REPORT_EXIT_KEYBOARD: | ||
632 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
633 | "REPORT_EXIT_KEYBOARD", report->id, raw_size-1); | ||
634 | hid_debug_event(hdev, buff); | ||
635 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
636 | raw_data[1] | (raw_data[2] << 8), | ||
637 | raw_data[2], raw_data[1]); | ||
638 | hid_debug_event(hdev, buff); | ||
639 | break; | ||
640 | case REPORT_VERSION: | ||
641 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
642 | "REPORT_VERSION", report->id, raw_size-1); | ||
643 | hid_debug_event(hdev, buff); | ||
644 | break; | ||
645 | case REPORT_DEVID: | ||
646 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
647 | "REPORT_DEVID", report->id, raw_size-1); | ||
648 | hid_debug_event(hdev, buff); | ||
649 | break; | ||
650 | case REPORT_SPLASH_SIZE: | ||
651 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
652 | "REPORT_SPLASH_SIZE", report->id, raw_size-1); | ||
653 | hid_debug_event(hdev, buff); | ||
654 | break; | ||
655 | case REPORT_HOOK_VERSION: | ||
656 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
657 | "REPORT_HOOK_VERSION", report->id, raw_size-1); | ||
658 | hid_debug_event(hdev, buff); | ||
659 | break; | ||
660 | case REPORT_EXIT_FLASHER: | ||
661 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
662 | "REPORT_VERSION", report->id, raw_size-1); | ||
663 | hid_debug_event(hdev, buff); | ||
664 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
665 | raw_data[1] | (raw_data[2] << 8), | ||
666 | raw_data[2], raw_data[1]); | ||
667 | hid_debug_event(hdev, buff); | ||
668 | break; | ||
669 | default: | ||
670 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
671 | "<unknown>", report->id, raw_size-1); | ||
672 | hid_debug_event(hdev, buff); | ||
673 | break; | ||
674 | } | ||
675 | wake_up_interruptible(&hdev->debug_wait); | ||
676 | kfree(buff); | ||
677 | } | ||
678 | |||
679 | static void picolcd_debug_raw_event(struct picolcd_data *data, | ||
680 | struct hid_device *hdev, struct hid_report *report, | ||
681 | u8 *raw_data, int size) | ||
682 | { | ||
683 | char *buff; | ||
684 | |||
685 | #define BUFF_SZ 256 | ||
686 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
687 | if (!hdev->debug_events) | ||
688 | return; | ||
689 | |||
690 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
691 | if (!buff) | ||
692 | return; | ||
693 | |||
694 | switch (report->id) { | ||
695 | case REPORT_ERROR_CODE: | ||
696 | /* 2 data bytes with affected report and error code */ | ||
697 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
698 | "REPORT_ERROR_CODE", report->id, size-1); | ||
699 | hid_debug_event(hdev, buff); | ||
700 | if (raw_data[2] < ARRAY_SIZE(error_codes)) | ||
701 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n", | ||
702 | raw_data[2], error_codes[raw_data[2]], raw_data[1]); | ||
703 | else | ||
704 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n", | ||
705 | raw_data[2], raw_data[1]); | ||
706 | hid_debug_event(hdev, buff); | ||
707 | break; | ||
708 | case REPORT_KEY_STATE: | ||
709 | /* 2 data bytes with key state */ | ||
710 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
711 | "REPORT_KEY_STATE", report->id, size-1); | ||
712 | hid_debug_event(hdev, buff); | ||
713 | if (raw_data[1] == 0) | ||
714 | snprintf(buff, BUFF_SZ, "\tNo key pressed\n"); | ||
715 | else if (raw_data[2] == 0) | ||
716 | snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n", | ||
717 | raw_data[1], raw_data[1]); | ||
718 | else | ||
719 | snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n", | ||
720 | raw_data[1], raw_data[1], raw_data[2], raw_data[2]); | ||
721 | hid_debug_event(hdev, buff); | ||
722 | break; | ||
723 | case REPORT_IR_DATA: | ||
724 | /* Up to 20 byes of IR scancode data */ | ||
725 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
726 | "REPORT_IR_DATA", report->id, size-1); | ||
727 | hid_debug_event(hdev, buff); | ||
728 | if (raw_data[1] == 0) { | ||
729 | snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n"); | ||
730 | hid_debug_event(hdev, buff); | ||
731 | } else if (raw_data[1] + 1 <= size) { | ||
732 | snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ", | ||
733 | raw_data[1]-1); | ||
734 | hid_debug_event(hdev, buff); | ||
735 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1); | ||
736 | hid_debug_event(hdev, buff); | ||
737 | } else { | ||
738 | snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n", | ||
739 | raw_data[1]-1); | ||
740 | hid_debug_event(hdev, buff); | ||
741 | } | ||
742 | break; | ||
743 | case REPORT_EE_DATA: | ||
744 | /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */ | ||
745 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
746 | "REPORT_EE_DATA", report->id, size-1); | ||
747 | hid_debug_event(hdev, buff); | ||
748 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
749 | raw_data[2], raw_data[1]); | ||
750 | hid_debug_event(hdev, buff); | ||
751 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
752 | hid_debug_event(hdev, buff); | ||
753 | if (raw_data[3] == 0) { | ||
754 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
755 | hid_debug_event(hdev, buff); | ||
756 | } else if (raw_data[3] + 4 <= size) { | ||
757 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
758 | hid_debug_event(hdev, buff); | ||
759 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
760 | hid_debug_event(hdev, buff); | ||
761 | } else { | ||
762 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
763 | hid_debug_event(hdev, buff); | ||
764 | } | ||
765 | break; | ||
766 | case REPORT_MEMORY: | ||
767 | /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */ | ||
768 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
769 | "REPORT_MEMORY", report->id, size-1); | ||
770 | hid_debug_event(hdev, buff); | ||
771 | switch (data->addr_sz) { | ||
772 | case 2: | ||
773 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
774 | raw_data[2], raw_data[1]); | ||
775 | hid_debug_event(hdev, buff); | ||
776 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
777 | hid_debug_event(hdev, buff); | ||
778 | if (raw_data[3] == 0) { | ||
779 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
780 | } else if (raw_data[3] + 4 <= size) { | ||
781 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
782 | hid_debug_event(hdev, buff); | ||
783 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
784 | } else { | ||
785 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
786 | } | ||
787 | break; | ||
788 | case 3: | ||
789 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
790 | raw_data[3], raw_data[2], raw_data[1]); | ||
791 | hid_debug_event(hdev, buff); | ||
792 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
793 | hid_debug_event(hdev, buff); | ||
794 | if (raw_data[4] == 0) { | ||
795 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
796 | } else if (raw_data[4] + 5 <= size) { | ||
797 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
798 | hid_debug_event(hdev, buff); | ||
799 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
800 | } else { | ||
801 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
802 | } | ||
803 | break; | ||
804 | default: | ||
805 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
806 | } | ||
807 | hid_debug_event(hdev, buff); | ||
808 | break; | ||
809 | case REPORT_VERSION: | ||
810 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
811 | "REPORT_VERSION", report->id, size-1); | ||
812 | hid_debug_event(hdev, buff); | ||
813 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
814 | raw_data[2], raw_data[1]); | ||
815 | hid_debug_event(hdev, buff); | ||
816 | break; | ||
817 | case REPORT_BL_ERASE_MEMORY: | ||
818 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
819 | "REPORT_BL_ERASE_MEMORY", report->id, size-1); | ||
820 | hid_debug_event(hdev, buff); | ||
821 | /* TODO */ | ||
822 | break; | ||
823 | case REPORT_BL_READ_MEMORY: | ||
824 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
825 | "REPORT_BL_READ_MEMORY", report->id, size-1); | ||
826 | hid_debug_event(hdev, buff); | ||
827 | /* TODO */ | ||
828 | break; | ||
829 | case REPORT_BL_WRITE_MEMORY: | ||
830 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
831 | "REPORT_BL_WRITE_MEMORY", report->id, size-1); | ||
832 | hid_debug_event(hdev, buff); | ||
833 | /* TODO */ | ||
834 | break; | ||
835 | case REPORT_DEVID: | ||
836 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
837 | "REPORT_DEVID", report->id, size-1); | ||
838 | hid_debug_event(hdev, buff); | ||
839 | snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n", | ||
840 | raw_data[1], raw_data[2], raw_data[3], raw_data[4]); | ||
841 | hid_debug_event(hdev, buff); | ||
842 | snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n", | ||
843 | raw_data[5]); | ||
844 | hid_debug_event(hdev, buff); | ||
845 | break; | ||
846 | case REPORT_SPLASH_SIZE: | ||
847 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
848 | "REPORT_SPLASH_SIZE", report->id, size-1); | ||
849 | hid_debug_event(hdev, buff); | ||
850 | snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n", | ||
851 | (raw_data[2] << 8) | raw_data[1]); | ||
852 | hid_debug_event(hdev, buff); | ||
853 | snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n", | ||
854 | (raw_data[4] << 8) | raw_data[3]); | ||
855 | hid_debug_event(hdev, buff); | ||
856 | break; | ||
857 | case REPORT_HOOK_VERSION: | ||
858 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
859 | "REPORT_HOOK_VERSION", report->id, size-1); | ||
860 | hid_debug_event(hdev, buff); | ||
861 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
862 | raw_data[1], raw_data[2]); | ||
863 | hid_debug_event(hdev, buff); | ||
864 | break; | ||
865 | default: | ||
866 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
867 | "<unknown>", report->id, size-1); | ||
868 | hid_debug_event(hdev, buff); | ||
869 | break; | ||
870 | } | ||
871 | wake_up_interruptible(&hdev->debug_wait); | ||
872 | kfree(buff); | ||
873 | } | ||
874 | #else | ||
875 | #define picolcd_debug_raw_event(data, hdev, report, raw_data, size) | ||
876 | #endif | ||
877 | |||
878 | /* | ||
879 | * Handle raw report as sent by device | ||
880 | */ | ||
881 | static int picolcd_raw_event(struct hid_device *hdev, | ||
882 | struct hid_report *report, u8 *raw_data, int size) | ||
883 | { | ||
884 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
885 | unsigned long flags; | ||
886 | int ret = 0; | ||
887 | |||
888 | if (!data) | ||
889 | return 1; | ||
890 | |||
891 | if (report->id == REPORT_KEY_STATE) { | ||
892 | if (data->input_keys) | ||
893 | ret = picolcd_raw_keypad(data, report, raw_data+1, size-1); | ||
894 | } else if (report->id == REPORT_IR_DATA) { | ||
895 | if (data->input_cir) | ||
896 | ret = picolcd_raw_cir(data, report, raw_data+1, size-1); | ||
897 | } else { | ||
898 | spin_lock_irqsave(&data->lock, flags); | ||
899 | /* | ||
900 | * We let the caller of picolcd_send_and_wait() check if the | ||
901 | * report we got is one of the expected ones or not. | ||
902 | */ | ||
903 | if (data->pending) { | ||
904 | memcpy(data->pending->raw_data, raw_data+1, size-1); | ||
905 | data->pending->raw_size = size-1; | ||
906 | data->pending->in_report = report; | ||
907 | complete(&data->pending->ready); | ||
908 | } | ||
909 | spin_unlock_irqrestore(&data->lock, flags); | ||
910 | } | ||
911 | |||
912 | picolcd_debug_raw_event(data, hdev, report, raw_data, size); | ||
913 | return 1; | ||
914 | } | ||
915 | |||
916 | /* initialize keypad input device */ | ||
917 | static int picolcd_init_keys(struct picolcd_data *data, | ||
918 | struct hid_report *report) | ||
919 | { | ||
920 | struct hid_device *hdev = data->hdev; | ||
921 | struct input_dev *idev; | ||
922 | int error, i; | ||
923 | |||
924 | if (!report) | ||
925 | return -ENODEV; | ||
926 | if (report->maxfield != 1 || report->field[0]->report_count != 2 || | ||
927 | report->field[0]->report_size != 8) { | ||
928 | dev_err(&hdev->dev, "unsupported KEY_STATE report"); | ||
929 | return -EINVAL; | ||
930 | } | ||
931 | |||
932 | idev = input_allocate_device(); | ||
933 | if (idev == NULL) { | ||
934 | dev_err(&hdev->dev, "failed to allocate input device"); | ||
935 | return -ENOMEM; | ||
936 | } | ||
937 | input_set_drvdata(idev, hdev); | ||
938 | memcpy(data->keycode, def_keymap, sizeof(def_keymap)); | ||
939 | idev->name = hdev->name; | ||
940 | idev->phys = hdev->phys; | ||
941 | idev->uniq = hdev->uniq; | ||
942 | idev->id.bustype = hdev->bus; | ||
943 | idev->id.vendor = hdev->vendor; | ||
944 | idev->id.product = hdev->product; | ||
945 | idev->id.version = hdev->version; | ||
946 | idev->dev.parent = hdev->dev.parent; | ||
947 | idev->keycode = &data->keycode; | ||
948 | idev->keycodemax = PICOLCD_KEYS; | ||
949 | idev->keycodesize = sizeof(data->keycode[0]); | ||
950 | input_set_capability(idev, EV_MSC, MSC_SCAN); | ||
951 | set_bit(EV_REP, idev->evbit); | ||
952 | for (i = 0; i < PICOLCD_KEYS; i++) | ||
953 | input_set_capability(idev, EV_KEY, data->keycode[i]); | ||
954 | error = input_register_device(idev); | ||
955 | if (error) { | ||
956 | dev_err(&hdev->dev, "error registering the input device"); | ||
957 | input_free_device(idev); | ||
958 | return error; | ||
959 | } | ||
960 | data->input_keys = idev; | ||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static void picolcd_exit_keys(struct picolcd_data *data) | ||
965 | { | ||
966 | struct input_dev *idev = data->input_keys; | ||
967 | |||
968 | data->input_keys = NULL; | ||
969 | if (idev) | ||
970 | input_unregister_device(idev); | ||
971 | } | ||
972 | |||
973 | /* initialize CIR input device */ | ||
974 | static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) | ||
975 | { | ||
976 | /* support not implemented yet */ | ||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | static inline void picolcd_exit_cir(struct picolcd_data *data) | ||
981 | { | ||
982 | } | ||
983 | |||
984 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | ||
985 | { | ||
986 | struct hid_report *report; | ||
987 | int error; | ||
988 | |||
989 | error = picolcd_check_version(hdev); | ||
990 | if (error) | ||
991 | return error; | ||
992 | |||
993 | if (data->version[0] != 0 && data->version[1] != 3) | ||
994 | dev_info(&hdev->dev, "Device with untested firmware revision, " | ||
995 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
996 | dev_name(&hdev->dev)); | ||
997 | |||
998 | /* Setup keypad input device */ | ||
999 | error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev)); | ||
1000 | if (error) | ||
1001 | goto err; | ||
1002 | |||
1003 | /* Setup CIR input device */ | ||
1004 | error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev)); | ||
1005 | if (error) | ||
1006 | goto err; | ||
1007 | |||
1008 | #ifdef CONFIG_DEBUG_FS | ||
1009 | report = picolcd_out_report(REPORT_READ_MEMORY, hdev); | ||
1010 | if (report && report->maxfield == 1 && report->field[0]->report_size == 8) | ||
1011 | data->addr_sz = report->field[0]->report_count - 1; | ||
1012 | else | ||
1013 | data->addr_sz = -1; | ||
1014 | #endif | ||
1015 | return 0; | ||
1016 | err: | ||
1017 | picolcd_exit_cir(data); | ||
1018 | picolcd_exit_keys(data); | ||
1019 | return error; | ||
1020 | } | ||
1021 | |||
1022 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) | ||
1023 | { | ||
1024 | struct hid_report *report; | ||
1025 | int error; | ||
1026 | |||
1027 | error = picolcd_check_version(hdev); | ||
1028 | if (error) | ||
1029 | return error; | ||
1030 | |||
1031 | if (data->version[0] != 1 && data->version[1] != 0) | ||
1032 | dev_info(&hdev->dev, "Device with untested bootloader revision, " | ||
1033 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
1034 | dev_name(&hdev->dev)); | ||
1035 | |||
1036 | #ifdef CONFIG_DEBUG_FS | ||
1037 | report = picolcd_out_report(REPORT_BL_READ_MEMORY, hdev); | ||
1038 | if (report && report->maxfield == 1 && report->field[0]->report_size == 8) | ||
1039 | data->addr_sz = report->field[0]->report_count - 1; | ||
1040 | else | ||
1041 | data->addr_sz = -1; | ||
1042 | #endif | ||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | static int picolcd_probe(struct hid_device *hdev, | ||
1047 | const struct hid_device_id *id) | ||
1048 | { | ||
1049 | struct picolcd_data *data; | ||
1050 | int error = -ENOMEM; | ||
1051 | |||
1052 | dbg_hid(PICOLCD_NAME " hardware probe...\n"); | ||
1053 | |||
1054 | /* | ||
1055 | * Let's allocate the picolcd data structure, set some reasonable | ||
1056 | * defaults, and associate it with the device | ||
1057 | */ | ||
1058 | data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL); | ||
1059 | if (data == NULL) { | ||
1060 | dev_err(&hdev->dev, "can't allocate space for Minibox PicoLCD device data\n"); | ||
1061 | error = -ENOMEM; | ||
1062 | goto err_no_cleanup; | ||
1063 | } | ||
1064 | |||
1065 | spin_lock_init(&data->lock); | ||
1066 | mutex_init(&data->mutex); | ||
1067 | data->hdev = hdev; | ||
1068 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
1069 | data->status |= PICOLCD_BOOTLOADER; | ||
1070 | hid_set_drvdata(hdev, data); | ||
1071 | |||
1072 | /* Parse the device reports and start it up */ | ||
1073 | error = hid_parse(hdev); | ||
1074 | if (error) { | ||
1075 | dev_err(&hdev->dev, "device report parse failed\n"); | ||
1076 | goto err_cleanup_data; | ||
1077 | } | ||
1078 | |||
1079 | /* We don't use hidinput but hid_hw_start() fails if nothing is | ||
1080 | * claimed. So spoof claimed input. */ | ||
1081 | hdev->claimed = HID_CLAIMED_INPUT; | ||
1082 | error = hid_hw_start(hdev, 0); | ||
1083 | hdev->claimed = 0; | ||
1084 | if (error) { | ||
1085 | dev_err(&hdev->dev, "hardware start failed\n"); | ||
1086 | goto err_cleanup_data; | ||
1087 | } | ||
1088 | |||
1089 | error = hdev->ll_driver->open(hdev); | ||
1090 | if (error) { | ||
1091 | dev_err(&hdev->dev, "failed to open input interrupt pipe for key and IR events\n"); | ||
1092 | goto err_cleanup_hid_hw; | ||
1093 | } | ||
1094 | |||
1095 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode); | ||
1096 | if (error) { | ||
1097 | dev_err(&hdev->dev, "failed to create sysfs attributes\n"); | ||
1098 | goto err_cleanup_hid_ll; | ||
1099 | } | ||
1100 | |||
1101 | if (data->status & PICOLCD_BOOTLOADER) | ||
1102 | error = picolcd_probe_bootloader(hdev, data); | ||
1103 | else | ||
1104 | error = picolcd_probe_lcd(hdev, data); | ||
1105 | if (error) | ||
1106 | goto err_cleanup_sysfs; | ||
1107 | |||
1108 | dbg_hid(PICOLCD_NAME " activated and initialized\n"); | ||
1109 | return 0; | ||
1110 | |||
1111 | err_cleanup_sysfs: | ||
1112 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
1113 | err_cleanup_hid_ll: | ||
1114 | hdev->ll_driver->close(hdev); | ||
1115 | err_cleanup_hid_hw: | ||
1116 | hid_hw_stop(hdev); | ||
1117 | err_cleanup_data: | ||
1118 | kfree(data); | ||
1119 | err_no_cleanup: | ||
1120 | hid_set_drvdata(hdev, NULL); | ||
1121 | |||
1122 | return error; | ||
1123 | } | ||
1124 | |||
1125 | static void picolcd_remove(struct hid_device *hdev) | ||
1126 | { | ||
1127 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
1128 | unsigned long flags; | ||
1129 | |||
1130 | dbg_hid(PICOLCD_NAME " hardware remove...\n"); | ||
1131 | spin_lock_irqsave(&data->lock, flags); | ||
1132 | data->status |= PICOLCD_FAILED; | ||
1133 | spin_unlock_irqrestore(&data->lock, flags); | ||
1134 | |||
1135 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
1136 | hdev->ll_driver->close(hdev); | ||
1137 | hid_hw_stop(hdev); | ||
1138 | hid_set_drvdata(hdev, NULL); | ||
1139 | |||
1140 | /* Shortcut potential pending reply that will never arrive */ | ||
1141 | spin_lock_irqsave(&data->lock, flags); | ||
1142 | if (data->pending) | ||
1143 | complete(&data->pending->ready); | ||
1144 | spin_unlock_irqrestore(&data->lock, flags); | ||
1145 | |||
1146 | /* Cleanup input */ | ||
1147 | picolcd_exit_cir(data); | ||
1148 | picolcd_exit_keys(data); | ||
1149 | |||
1150 | mutex_destroy(&data->mutex); | ||
1151 | /* Finally, clean up the picolcd data itself */ | ||
1152 | kfree(data); | ||
1153 | } | ||
1154 | |||
1155 | static const struct hid_device_id picolcd_devices[] = { | ||
1156 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
1157 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
1158 | { } | ||
1159 | }; | ||
1160 | MODULE_DEVICE_TABLE(hid, picolcd_devices); | ||
1161 | |||
1162 | static struct hid_driver picolcd_driver = { | ||
1163 | .name = "hid-picolcd", | ||
1164 | .id_table = picolcd_devices, | ||
1165 | .probe = picolcd_probe, | ||
1166 | .remove = picolcd_remove, | ||
1167 | .raw_event = picolcd_raw_event, | ||
1168 | }; | ||
1169 | |||
1170 | static int __init picolcd_init(void) | ||
1171 | { | ||
1172 | return hid_register_driver(&picolcd_driver); | ||
1173 | } | ||
1174 | |||
1175 | static void __exit picolcd_exit(void) | ||
1176 | { | ||
1177 | hid_unregister_driver(&picolcd_driver); | ||
1178 | } | ||
1179 | |||
1180 | module_init(picolcd_init); | ||
1181 | module_exit(picolcd_exit); | ||
1182 | MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver"); | ||
1183 | MODULE_LICENSE("GPL v2"); | ||