diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 58 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 2 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd.c | 2562 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 1 |
6 files changed, 2626 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 71d4c0703629..0e8aa63e3f55 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -262,6 +262,64 @@ config HID_PETALYNX | |||
262 | ---help--- | 262 | ---help--- |
263 | Support for Petalynx Maxter remote control. | 263 | Support for Petalynx Maxter remote control. |
264 | 264 | ||
265 | config HID_PICOLCD | ||
266 | tristate "PicoLCD (graphic version)" | ||
267 | depends on USB_HID | ||
268 | ---help--- | ||
269 | This provides support for Minibox PicoLCD devices, currently | ||
270 | only the graphical ones are supported. | ||
271 | |||
272 | This includes support for the following device features: | ||
273 | - Keypad | ||
274 | - Switching between Firmware and Flash mode | ||
275 | - EEProm / Flash access (via debugfs) | ||
276 | Features selectively enabled: | ||
277 | - Framebuffer for monochrome 256x64 display | ||
278 | - Backlight control | ||
279 | - Contrast control | ||
280 | - General purpose outputs | ||
281 | Features that are not (yet) supported: | ||
282 | - IR | ||
283 | |||
284 | config HID_PICOLCD_FB | ||
285 | bool "Framebuffer support" if EMBEDDED | ||
286 | default !EMBEDDED | ||
287 | depends on HID_PICOLCD | ||
288 | depends on HID_PICOLCD=FB || FB=y | ||
289 | select FB_DEFERRED_IO | ||
290 | select FB_SYS_FILLRECT | ||
291 | select FB_SYS_COPYAREA | ||
292 | select FB_SYS_IMAGEBLIT | ||
293 | select FB_SYS_FOPS | ||
294 | ---help--- | ||
295 | Provide access to PicoLCD's 256x64 monochrome display via a | ||
296 | frambuffer device. | ||
297 | |||
298 | config HID_PICOLCD_BACKLIGHT | ||
299 | bool "Backlight control" if EMBEDDED | ||
300 | default !EMBEDDED | ||
301 | depends on HID_PICOLCD | ||
302 | depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y | ||
303 | ---help--- | ||
304 | Provide access to PicoLCD's backlight control via backlight | ||
305 | class. | ||
306 | |||
307 | config HID_PICOLCD_LCD | ||
308 | bool "Contrast control" if EMBEDDED | ||
309 | default !EMBEDDED | ||
310 | depends on HID_PICOLCD | ||
311 | depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y | ||
312 | ---help--- | ||
313 | Provide access to PicoLCD's LCD contrast via lcd class. | ||
314 | |||
315 | config HID_PICOLCD_LEDS | ||
316 | bool "GPO via leds class" if EMBEDDED | ||
317 | default !EMBEDDED | ||
318 | depends on HID_PICOLCD | ||
319 | depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y | ||
320 | ---help--- | ||
321 | Provide access to PicoLCD's GPO pins via leds class. | ||
322 | |||
265 | config HID_QUANTA | 323 | config HID_QUANTA |
266 | tristate "Quanta Optical Touch" | 324 | tristate "Quanta Optical Touch" |
267 | depends on USB_HID | 325 | depends on USB_HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0b2618f092ca..7fd1614e7e2c 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -44,6 +44,7 @@ obj-$(CONFIG_HID_ORTEK) += hid-ortek.o | |||
44 | obj-$(CONFIG_HID_QUANTA) += hid-quanta.o | 44 | obj-$(CONFIG_HID_QUANTA) += hid-quanta.o |
45 | obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o | 45 | obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o |
46 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o | 46 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o |
47 | obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o | ||
47 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o | 48 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o |
48 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o | 49 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o |
49 | obj-$(CONFIG_HID_SONY) += hid-sony.o | 50 | obj-$(CONFIG_HID_SONY) += hid-sony.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2e2aa759d230..bb11fb460d55 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1335,6 +1335,8 @@ static const struct hid_device_id hid_blacklist[] = { | |||
1335 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, | 1335 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, |
1336 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, | 1336 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, |
1337 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, | 1337 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, |
1338 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
1339 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
1338 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, | 1340 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, |
1339 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, | 1341 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, |
1340 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, | 1342 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 797e06470356..783b41d8592e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -341,6 +341,8 @@ | |||
341 | #define USB_VENDOR_ID_MICROCHIP 0x04d8 | 341 | #define USB_VENDOR_ID_MICROCHIP 0x04d8 |
342 | #define USB_DEVICE_ID_PICKIT1 0x0032 | 342 | #define USB_DEVICE_ID_PICKIT1 0x0032 |
343 | #define USB_DEVICE_ID_PICKIT2 0x0033 | 343 | #define USB_DEVICE_ID_PICKIT2 0x0033 |
344 | #define USB_DEVICE_ID_PICOLCD 0xc002 | ||
345 | #define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 | ||
344 | 346 | ||
345 | #define USB_VENDOR_ID_MICROSOFT 0x045e | 347 | #define USB_VENDOR_ID_MICROSOFT 0x045e |
346 | #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b | 348 | #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b |
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c new file mode 100644 index 000000000000..aa6f2e18d4a6 --- /dev/null +++ b/drivers/hid/hid-picolcd.c | |||
@@ -0,0 +1,2562 @@ | |||
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/fb.h> | ||
28 | #include <linux/vmalloc.h> | ||
29 | #include <linux/backlight.h> | ||
30 | #include <linux/lcd.h> | ||
31 | |||
32 | #include <linux/leds.h> | ||
33 | |||
34 | #include <linux/seq_file.h> | ||
35 | #include <linux/debugfs.h> | ||
36 | |||
37 | #include <linux/completion.h> | ||
38 | #include <linux/uaccess.h> | ||
39 | |||
40 | #define PICOLCD_NAME "PicoLCD (graphic)" | ||
41 | |||
42 | /* Report numbers */ | ||
43 | #define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */ | ||
44 | #define ERR_SUCCESS 0x00 | ||
45 | #define ERR_PARAMETER_MISSING 0x01 | ||
46 | #define ERR_DATA_MISSING 0x02 | ||
47 | #define ERR_BLOCK_READ_ONLY 0x03 | ||
48 | #define ERR_BLOCK_NOT_ERASABLE 0x04 | ||
49 | #define ERR_BLOCK_TOO_BIG 0x05 | ||
50 | #define ERR_SECTION_OVERFLOW 0x06 | ||
51 | #define ERR_INVALID_CMD_LEN 0x07 | ||
52 | #define ERR_INVALID_DATA_LEN 0x08 | ||
53 | #define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */ | ||
54 | #define REPORT_IR_DATA 0x21 /* LCD: IN[63] */ | ||
55 | #define REPORT_EE_DATA 0x32 /* LCD: IN[63] */ | ||
56 | #define REPORT_MEMORY 0x41 /* LCD: IN[63] */ | ||
57 | #define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */ | ||
58 | #define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */ | ||
59 | #define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */ | ||
60 | #define REPORT_RESET 0x93 /* LCD: OUT[2] */ | ||
61 | #define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */ | ||
62 | #define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */ | ||
63 | #define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */ | ||
64 | #define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */ | ||
65 | #define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */ | ||
66 | #define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */ | ||
67 | #define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */ | ||
68 | #define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */ | ||
69 | #define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */ | ||
70 | #define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */ | ||
71 | #define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */ | ||
72 | #define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */ | ||
73 | #define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */ | ||
74 | #define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */ | ||
75 | #define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */ | ||
76 | #define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */ | ||
77 | #define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */ | ||
78 | #define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */ | ||
79 | |||
80 | #ifdef CONFIG_HID_PICOLCD_FB | ||
81 | /* Framebuffer | ||
82 | * | ||
83 | * The PicoLCD use a Topway LCD module of 256x64 pixel | ||
84 | * This display area is tiled over 4 controllers with 8 tiles | ||
85 | * each. Each tile has 8x64 pixel, each data byte representing | ||
86 | * a 1-bit wide vertical line of the tile. | ||
87 | * | ||
88 | * The display can be updated at a tile granularity. | ||
89 | * | ||
90 | * Chip 1 Chip 2 Chip 3 Chip 4 | ||
91 | * +----------------+----------------+----------------+----------------+ | ||
92 | * | Tile 1 | Tile 1 | Tile 1 | Tile 1 | | ||
93 | * +----------------+----------------+----------------+----------------+ | ||
94 | * | Tile 2 | Tile 2 | Tile 2 | Tile 2 | | ||
95 | * +----------------+----------------+----------------+----------------+ | ||
96 | * ... | ||
97 | * +----------------+----------------+----------------+----------------+ | ||
98 | * | Tile 8 | Tile 8 | Tile 8 | Tile 8 | | ||
99 | * +----------------+----------------+----------------+----------------+ | ||
100 | */ | ||
101 | #define PICOLCDFB_NAME "picolcdfb" | ||
102 | #define PICOLCDFB_WIDTH (256) | ||
103 | #define PICOLCDFB_HEIGHT (64) | ||
104 | #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8) | ||
105 | |||
106 | #define PICOLCDFB_UPDATE_RATE_LIMIT 10 | ||
107 | #define PICOLCDFB_UPDATE_RATE_DEFAULT 2 | ||
108 | |||
109 | /* Framebuffer visual structures */ | ||
110 | static const struct fb_fix_screeninfo picolcdfb_fix = { | ||
111 | .id = PICOLCDFB_NAME, | ||
112 | .type = FB_TYPE_PACKED_PIXELS, | ||
113 | .visual = FB_VISUAL_MONO01, | ||
114 | .xpanstep = 0, | ||
115 | .ypanstep = 0, | ||
116 | .ywrapstep = 0, | ||
117 | .line_length = PICOLCDFB_WIDTH / 8, | ||
118 | .accel = FB_ACCEL_NONE, | ||
119 | }; | ||
120 | |||
121 | static const struct fb_var_screeninfo picolcdfb_var = { | ||
122 | .xres = PICOLCDFB_WIDTH, | ||
123 | .yres = PICOLCDFB_HEIGHT, | ||
124 | .xres_virtual = PICOLCDFB_WIDTH, | ||
125 | .yres_virtual = PICOLCDFB_HEIGHT, | ||
126 | .width = 103, | ||
127 | .height = 26, | ||
128 | .bits_per_pixel = 1, | ||
129 | .grayscale = 1, | ||
130 | }; | ||
131 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
132 | |||
133 | /* Input device | ||
134 | * | ||
135 | * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys | ||
136 | * and header for 4x4 key matrix. The built-in keys are part of the matrix. | ||
137 | */ | ||
138 | static const unsigned short def_keymap[] = { | ||
139 | KEY_RESERVED, /* none */ | ||
140 | KEY_BACK, /* col 4 + row 1 */ | ||
141 | KEY_HOMEPAGE, /* col 3 + row 1 */ | ||
142 | KEY_RESERVED, /* col 2 + row 1 */ | ||
143 | KEY_RESERVED, /* col 1 + row 1 */ | ||
144 | KEY_SCROLLUP, /* col 4 + row 2 */ | ||
145 | KEY_OK, /* col 3 + row 2 */ | ||
146 | KEY_SCROLLDOWN, /* col 2 + row 2 */ | ||
147 | KEY_RESERVED, /* col 1 + row 2 */ | ||
148 | KEY_RESERVED, /* col 4 + row 3 */ | ||
149 | KEY_RESERVED, /* col 3 + row 3 */ | ||
150 | KEY_RESERVED, /* col 2 + row 3 */ | ||
151 | KEY_RESERVED, /* col 1 + row 3 */ | ||
152 | KEY_RESERVED, /* col 4 + row 4 */ | ||
153 | KEY_RESERVED, /* col 3 + row 4 */ | ||
154 | KEY_RESERVED, /* col 2 + row 4 */ | ||
155 | KEY_RESERVED, /* col 1 + row 4 */ | ||
156 | }; | ||
157 | #define PICOLCD_KEYS ARRAY_SIZE(def_keymap) | ||
158 | |||
159 | /* Description of in-progress IO operation, used for operations | ||
160 | * that trigger response from device */ | ||
161 | struct picolcd_pending { | ||
162 | struct hid_report *out_report; | ||
163 | struct hid_report *in_report; | ||
164 | struct completion ready; | ||
165 | int raw_size; | ||
166 | u8 raw_data[64]; | ||
167 | }; | ||
168 | |||
169 | /* Per device data structure */ | ||
170 | struct picolcd_data { | ||
171 | struct hid_device *hdev; | ||
172 | #ifdef CONFIG_DEBUG_FS | ||
173 | struct dentry *debug_reset; | ||
174 | struct dentry *debug_eeprom; | ||
175 | struct dentry *debug_flash; | ||
176 | struct mutex mutex_flash; | ||
177 | int addr_sz; | ||
178 | #endif | ||
179 | u8 version[2]; | ||
180 | unsigned short opmode_delay; | ||
181 | /* input stuff */ | ||
182 | u8 pressed_keys[2]; | ||
183 | struct input_dev *input_keys; | ||
184 | struct input_dev *input_cir; | ||
185 | unsigned short keycode[PICOLCD_KEYS]; | ||
186 | |||
187 | #ifdef CONFIG_HID_PICOLCD_FB | ||
188 | /* Framebuffer stuff */ | ||
189 | u8 fb_update_rate; | ||
190 | u8 fb_bpp; | ||
191 | u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */ | ||
192 | u8 *fb_bitmap; /* framebuffer */ | ||
193 | struct fb_info *fb_info; | ||
194 | struct fb_deferred_io fb_defio; | ||
195 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
196 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
197 | struct lcd_device *lcd; | ||
198 | u8 lcd_contrast; | ||
199 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
200 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
201 | struct backlight_device *backlight; | ||
202 | u8 lcd_brightness; | ||
203 | u8 lcd_power; | ||
204 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
205 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
206 | /* LED stuff */ | ||
207 | u8 led_state; | ||
208 | struct led_classdev *led[8]; | ||
209 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
210 | |||
211 | /* Housekeeping stuff */ | ||
212 | spinlock_t lock; | ||
213 | struct mutex mutex; | ||
214 | struct picolcd_pending *pending; | ||
215 | int status; | ||
216 | #define PICOLCD_BOOTLOADER 1 | ||
217 | #define PICOLCD_FAILED 2 | ||
218 | #define PICOLCD_READY_FB 4 | ||
219 | }; | ||
220 | |||
221 | |||
222 | /* Find a given report */ | ||
223 | #define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT) | ||
224 | #define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT) | ||
225 | |||
226 | static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir) | ||
227 | { | ||
228 | struct list_head *feature_report_list = &hdev->report_enum[dir].report_list; | ||
229 | struct hid_report *report = NULL; | ||
230 | |||
231 | list_for_each_entry(report, feature_report_list, list) { | ||
232 | if (report->id == id) | ||
233 | return report; | ||
234 | } | ||
235 | dev_warn(&hdev->dev, "No report with id 0x%x found\n", id); | ||
236 | return NULL; | ||
237 | } | ||
238 | |||
239 | #ifdef CONFIG_DEBUG_FS | ||
240 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
241 | struct hid_device *hdev, struct hid_report *report); | ||
242 | #define usbhid_submit_report(a, b, c) \ | ||
243 | do { \ | ||
244 | picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ | ||
245 | usbhid_submit_report(a, b, c); \ | ||
246 | } while (0) | ||
247 | #endif | ||
248 | |||
249 | /* Submit a report and wait for a reply from device - if device fades away | ||
250 | * or does not respond in time, return NULL */ | ||
251 | static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | ||
252 | int report_id, const u8 *raw_data, int size) | ||
253 | { | ||
254 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
255 | struct picolcd_pending *work; | ||
256 | struct hid_report *report = picolcd_out_report(report_id, hdev); | ||
257 | unsigned long flags; | ||
258 | int i, j, k; | ||
259 | |||
260 | if (!report || !data) | ||
261 | return NULL; | ||
262 | if (data->status & PICOLCD_FAILED) | ||
263 | return NULL; | ||
264 | work = kzalloc(sizeof(*work), GFP_KERNEL); | ||
265 | if (!work) | ||
266 | return NULL; | ||
267 | |||
268 | init_completion(&work->ready); | ||
269 | work->out_report = report; | ||
270 | work->in_report = NULL; | ||
271 | work->raw_size = 0; | ||
272 | |||
273 | mutex_lock(&data->mutex); | ||
274 | spin_lock_irqsave(&data->lock, flags); | ||
275 | for (i = k = 0; i < report->maxfield; i++) | ||
276 | for (j = 0; j < report->field[i]->report_count; j++) { | ||
277 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); | ||
278 | k++; | ||
279 | } | ||
280 | data->pending = work; | ||
281 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
282 | spin_unlock_irqrestore(&data->lock, flags); | ||
283 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | ||
284 | spin_lock_irqsave(&data->lock, flags); | ||
285 | data->pending = NULL; | ||
286 | spin_unlock_irqrestore(&data->lock, flags); | ||
287 | mutex_unlock(&data->mutex); | ||
288 | return work; | ||
289 | } | ||
290 | |||
291 | #ifdef CONFIG_HID_PICOLCD_FB | ||
292 | /* Send a given tile to PicoLCD */ | ||
293 | static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile) | ||
294 | { | ||
295 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
296 | struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev); | ||
297 | struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev); | ||
298 | unsigned long flags; | ||
299 | u8 *tdata; | ||
300 | int i; | ||
301 | |||
302 | if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1) | ||
303 | return -ENODEV; | ||
304 | |||
305 | spin_lock_irqsave(&data->lock, flags); | ||
306 | hid_set_field(report1->field[0], 0, chip << 2); | ||
307 | hid_set_field(report1->field[0], 1, 0x02); | ||
308 | hid_set_field(report1->field[0], 2, 0x00); | ||
309 | hid_set_field(report1->field[0], 3, 0x00); | ||
310 | hid_set_field(report1->field[0], 4, 0xb8 | tile); | ||
311 | hid_set_field(report1->field[0], 5, 0x00); | ||
312 | hid_set_field(report1->field[0], 6, 0x00); | ||
313 | hid_set_field(report1->field[0], 7, 0x40); | ||
314 | hid_set_field(report1->field[0], 8, 0x00); | ||
315 | hid_set_field(report1->field[0], 9, 0x00); | ||
316 | hid_set_field(report1->field[0], 10, 32); | ||
317 | |||
318 | hid_set_field(report2->field[0], 0, (chip << 2) | 0x01); | ||
319 | hid_set_field(report2->field[0], 1, 0x00); | ||
320 | hid_set_field(report2->field[0], 2, 0x00); | ||
321 | hid_set_field(report2->field[0], 3, 32); | ||
322 | |||
323 | tdata = data->fb_vbitmap + (tile * 4 + chip) * 64; | ||
324 | for (i = 0; i < 64; i++) | ||
325 | if (i < 32) | ||
326 | hid_set_field(report1->field[0], 11 + i, tdata[i]); | ||
327 | else | ||
328 | hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); | ||
329 | |||
330 | usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); | ||
331 | usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); | ||
332 | spin_unlock_irqrestore(&data->lock, flags); | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | /* Translate a single tile*/ | ||
337 | static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp, | ||
338 | int chip, int tile) | ||
339 | { | ||
340 | int i, b, changed = 0; | ||
341 | u8 tdata[64]; | ||
342 | u8 *vdata = vbitmap + (tile * 4 + chip) * 64; | ||
343 | |||
344 | if (bpp == 1) { | ||
345 | for (b = 7; b >= 0; b--) { | ||
346 | const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32; | ||
347 | for (i = 0; i < 64; i++) { | ||
348 | tdata[i] <<= 1; | ||
349 | tdata[i] |= (bdata[i/8] >> (7 - i % 8)) & 0x01; | ||
350 | } | ||
351 | } | ||
352 | } else if (bpp == 8) { | ||
353 | for (b = 7; b >= 0; b--) { | ||
354 | const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8; | ||
355 | for (i = 0; i < 64; i++) { | ||
356 | tdata[i] <<= 1; | ||
357 | tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00; | ||
358 | } | ||
359 | } | ||
360 | } else { | ||
361 | /* Oops, we should never get here! */ | ||
362 | WARN_ON(1); | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | for (i = 0; i < 64; i++) | ||
367 | if (tdata[i] != vdata[i]) { | ||
368 | changed = 1; | ||
369 | vdata[i] = tdata[i]; | ||
370 | } | ||
371 | return changed; | ||
372 | } | ||
373 | |||
374 | /* Reconfigure LCD display */ | ||
375 | static int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
376 | { | ||
377 | struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); | ||
378 | int i, j; | ||
379 | unsigned long flags; | ||
380 | static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; | ||
381 | |||
382 | if (!report || report->maxfield != 1) | ||
383 | return -ENODEV; | ||
384 | |||
385 | spin_lock_irqsave(&data->lock, flags); | ||
386 | for (i = 0; i < 4; i++) { | ||
387 | for (j = 0; j < report->field[0]->maxusage; j++) | ||
388 | if (j == 0) | ||
389 | hid_set_field(report->field[0], j, i << 2); | ||
390 | else if (j < sizeof(mapcmd)) | ||
391 | hid_set_field(report->field[0], j, mapcmd[j]); | ||
392 | else | ||
393 | hid_set_field(report->field[0], j, 0); | ||
394 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
395 | } | ||
396 | |||
397 | data->status |= PICOLCD_READY_FB; | ||
398 | spin_unlock_irqrestore(&data->lock, flags); | ||
399 | |||
400 | if (data->fb_bitmap) { | ||
401 | if (clear) { | ||
402 | memset(data->fb_vbitmap, 0xff, PICOLCDFB_SIZE); | ||
403 | memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp); | ||
404 | } else { | ||
405 | /* invert 1 byte in each tile to force resend */ | ||
406 | for (i = 0; i < PICOLCDFB_SIZE; i += 64) | ||
407 | data->fb_vbitmap[i] = ~data->fb_vbitmap[i]; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /* schedule first output of framebuffer */ | ||
412 | if (data->fb_info) | ||
413 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
414 | |||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | /* Update fb_vbitmap from the screen_base and send changed tiles to device */ | ||
419 | static void picolcd_fb_update(struct picolcd_data *data) | ||
420 | { | ||
421 | int chip, tile, n; | ||
422 | unsigned long flags; | ||
423 | |||
424 | spin_lock_irqsave(&data->lock, flags); | ||
425 | if (!(data->status & PICOLCD_READY_FB)) { | ||
426 | spin_unlock_irqrestore(&data->lock, flags); | ||
427 | picolcd_fb_reset(data, 0); | ||
428 | } else { | ||
429 | spin_unlock_irqrestore(&data->lock, flags); | ||
430 | } | ||
431 | |||
432 | /* | ||
433 | * Translate the framebuffer into the format needed by the PicoLCD. | ||
434 | * See display layout above. | ||
435 | * Do this one tile after the other and push those tiles that changed. | ||
436 | * | ||
437 | * Wait for our IO to complete as otherwise we might flood the queue! | ||
438 | */ | ||
439 | n = 0; | ||
440 | for (chip = 0; chip < 4; chip++) | ||
441 | for (tile = 0; tile < 8; tile++) | ||
442 | if (picolcd_fb_update_tile(data->fb_vbitmap, | ||
443 | data->fb_bitmap, data->fb_bpp, chip, tile)) { | ||
444 | n += 2; | ||
445 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | ||
446 | usbhid_wait_io(data->hdev); | ||
447 | n = 0; | ||
448 | } | ||
449 | picolcd_fb_send_tile(data->hdev, chip, tile); | ||
450 | } | ||
451 | if (n) | ||
452 | usbhid_wait_io(data->hdev); | ||
453 | } | ||
454 | |||
455 | /* Stub to call the system default and update the image on the picoLCD */ | ||
456 | static void picolcd_fb_fillrect(struct fb_info *info, | ||
457 | const struct fb_fillrect *rect) | ||
458 | { | ||
459 | if (!info->par) | ||
460 | return; | ||
461 | sys_fillrect(info, rect); | ||
462 | |||
463 | schedule_delayed_work(&info->deferred_work, 0); | ||
464 | } | ||
465 | |||
466 | /* Stub to call the system default and update the image on the picoLCD */ | ||
467 | static void picolcd_fb_copyarea(struct fb_info *info, | ||
468 | const struct fb_copyarea *area) | ||
469 | { | ||
470 | if (!info->par) | ||
471 | return; | ||
472 | sys_copyarea(info, area); | ||
473 | |||
474 | schedule_delayed_work(&info->deferred_work, 0); | ||
475 | } | ||
476 | |||
477 | /* Stub to call the system default and update the image on the picoLCD */ | ||
478 | static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
479 | { | ||
480 | if (!info->par) | ||
481 | return; | ||
482 | sys_imageblit(info, image); | ||
483 | |||
484 | schedule_delayed_work(&info->deferred_work, 0); | ||
485 | } | ||
486 | |||
487 | /* | ||
488 | * this is the slow path from userspace. they can seek and write to | ||
489 | * the fb. it's inefficient to do anything less than a full screen draw | ||
490 | */ | ||
491 | static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf, | ||
492 | size_t count, loff_t *ppos) | ||
493 | { | ||
494 | ssize_t ret; | ||
495 | if (!info->par) | ||
496 | return -ENODEV; | ||
497 | ret = fb_sys_write(info, buf, count, ppos); | ||
498 | if (ret >= 0) | ||
499 | schedule_delayed_work(&info->deferred_work, 0); | ||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | static int picolcd_fb_blank(int blank, struct fb_info *info) | ||
504 | { | ||
505 | if (!info->par) | ||
506 | return -ENODEV; | ||
507 | /* We let fb notification do this for us via lcd/backlight device */ | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static void picolcd_fb_destroy(struct fb_info *info) | ||
512 | { | ||
513 | struct picolcd_data *data = info->par; | ||
514 | info->par = NULL; | ||
515 | if (data) | ||
516 | data->fb_info = NULL; | ||
517 | fb_deferred_io_cleanup(info); | ||
518 | framebuffer_release(info); | ||
519 | } | ||
520 | |||
521 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
522 | { | ||
523 | __u32 bpp = var->bits_per_pixel; | ||
524 | __u32 activate = var->activate; | ||
525 | |||
526 | /* only allow 1/8 bit depth (8-bit is grayscale) */ | ||
527 | *var = picolcdfb_var; | ||
528 | var->activate = activate; | ||
529 | if (bpp >= 8) | ||
530 | var->bits_per_pixel = 8; | ||
531 | else | ||
532 | var->bits_per_pixel = 1; | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int picolcd_set_par(struct fb_info *info) | ||
537 | { | ||
538 | struct picolcd_data *data = info->par; | ||
539 | u8 *o_fb, *n_fb; | ||
540 | if (info->var.bits_per_pixel == data->fb_bpp) | ||
541 | return 0; | ||
542 | /* switch between 1/8 bit depths */ | ||
543 | if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) | ||
544 | return -EINVAL; | ||
545 | |||
546 | o_fb = data->fb_bitmap; | ||
547 | n_fb = vmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel); | ||
548 | if (!n_fb) | ||
549 | return -ENOMEM; | ||
550 | |||
551 | fb_deferred_io_cleanup(info); | ||
552 | /* translate FB content to new bits-per-pixel */ | ||
553 | if (info->var.bits_per_pixel == 1) { | ||
554 | int i, b; | ||
555 | for (i = 0; i < PICOLCDFB_SIZE; i++) { | ||
556 | u8 p = 0; | ||
557 | for (b = 0; b < 8; b++) { | ||
558 | p <<= 1; | ||
559 | p |= o_fb[i*8+b] ? 0x01 : 0x00; | ||
560 | } | ||
561 | } | ||
562 | info->fix.visual = FB_VISUAL_MONO01; | ||
563 | info->fix.line_length = PICOLCDFB_WIDTH / 8; | ||
564 | } else { | ||
565 | int i; | ||
566 | for (i = 0; i < PICOLCDFB_SIZE * 8; i++) | ||
567 | n_fb[i] = o_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; | ||
568 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
569 | info->fix.line_length = PICOLCDFB_WIDTH; | ||
570 | } | ||
571 | |||
572 | data->fb_bitmap = n_fb; | ||
573 | data->fb_bpp = info->var.bits_per_pixel; | ||
574 | info->screen_base = (char __force __iomem *)n_fb; | ||
575 | info->fix.smem_start = (unsigned long)n_fb; | ||
576 | info->fix.smem_len = PICOLCDFB_SIZE*data->fb_bpp; | ||
577 | fb_deferred_io_init(info); | ||
578 | vfree(o_fb); | ||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | /* Note this can't be const because of struct fb_info definition */ | ||
583 | static struct fb_ops picolcdfb_ops = { | ||
584 | .owner = THIS_MODULE, | ||
585 | .fb_destroy = picolcd_fb_destroy, | ||
586 | .fb_read = fb_sys_read, | ||
587 | .fb_write = picolcd_fb_write, | ||
588 | .fb_blank = picolcd_fb_blank, | ||
589 | .fb_fillrect = picolcd_fb_fillrect, | ||
590 | .fb_copyarea = picolcd_fb_copyarea, | ||
591 | .fb_imageblit = picolcd_fb_imageblit, | ||
592 | .fb_check_var = picolcd_fb_check_var, | ||
593 | .fb_set_par = picolcd_set_par, | ||
594 | }; | ||
595 | |||
596 | |||
597 | /* Callback from deferred IO workqueue */ | ||
598 | static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) | ||
599 | { | ||
600 | picolcd_fb_update(info->par); | ||
601 | } | ||
602 | |||
603 | static const struct fb_deferred_io picolcd_fb_defio = { | ||
604 | .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT, | ||
605 | .deferred_io = picolcd_fb_deferred_io, | ||
606 | }; | ||
607 | |||
608 | |||
609 | /* | ||
610 | * The "fb_update_rate" sysfs attribute | ||
611 | */ | ||
612 | static ssize_t picolcd_fb_update_rate_show(struct device *dev, | ||
613 | struct device_attribute *attr, char *buf) | ||
614 | { | ||
615 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
616 | unsigned i, fb_update_rate = data->fb_update_rate; | ||
617 | size_t ret = 0; | ||
618 | |||
619 | for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) | ||
620 | if (ret >= PAGE_SIZE) | ||
621 | break; | ||
622 | else if (i == fb_update_rate) | ||
623 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i); | ||
624 | else | ||
625 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i); | ||
626 | if (ret > 0) | ||
627 | buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; | ||
628 | return ret; | ||
629 | } | ||
630 | |||
631 | static ssize_t picolcd_fb_update_rate_store(struct device *dev, | ||
632 | struct device_attribute *attr, const char *buf, size_t count) | ||
633 | { | ||
634 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
635 | int i; | ||
636 | unsigned u; | ||
637 | |||
638 | if (count < 1 || count > 10) | ||
639 | return -EINVAL; | ||
640 | |||
641 | i = sscanf(buf, "%u", &u); | ||
642 | if (i != 1) | ||
643 | return -EINVAL; | ||
644 | |||
645 | if (u > PICOLCDFB_UPDATE_RATE_LIMIT) | ||
646 | return -ERANGE; | ||
647 | else if (u == 0) | ||
648 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
649 | |||
650 | data->fb_update_rate = u; | ||
651 | data->fb_defio.delay = HZ / data->fb_update_rate; | ||
652 | return count; | ||
653 | } | ||
654 | |||
655 | static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, | ||
656 | picolcd_fb_update_rate_store); | ||
657 | |||
658 | /* initialize Framebuffer device */ | ||
659 | static int picolcd_init_framebuffer(struct picolcd_data *data) | ||
660 | { | ||
661 | struct device *dev = &data->hdev->dev; | ||
662 | struct fb_info *info = NULL; | ||
663 | int error = -ENOMEM; | ||
664 | u8 *fb_vbitmap = NULL; | ||
665 | u8 *fb_bitmap = NULL; | ||
666 | |||
667 | fb_bitmap = vmalloc(PICOLCDFB_SIZE*picolcdfb_var.bits_per_pixel); | ||
668 | if (fb_bitmap == NULL) { | ||
669 | dev_err(dev, "can't get a free page for framebuffer\n"); | ||
670 | goto err_nomem; | ||
671 | } | ||
672 | |||
673 | fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL); | ||
674 | if (fb_vbitmap == NULL) { | ||
675 | dev_err(dev, "can't alloc vbitmap image buffer\n"); | ||
676 | goto err_nomem; | ||
677 | } | ||
678 | |||
679 | data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
680 | data->fb_defio = picolcd_fb_defio; | ||
681 | info = framebuffer_alloc(0, dev); | ||
682 | if (info == NULL) { | ||
683 | dev_err(dev, "failed to allocate a framebuffer\n"); | ||
684 | goto err_nomem; | ||
685 | } | ||
686 | |||
687 | info->fbdefio = &data->fb_defio; | ||
688 | info->screen_base = (char __force __iomem *)fb_bitmap; | ||
689 | info->fbops = &picolcdfb_ops; | ||
690 | info->var = picolcdfb_var; | ||
691 | info->fix = picolcdfb_fix; | ||
692 | info->fix.smem_len = PICOLCDFB_SIZE; | ||
693 | info->fix.smem_start = (unsigned long)fb_bitmap; | ||
694 | info->par = data; | ||
695 | info->flags = FBINFO_FLAG_DEFAULT; | ||
696 | |||
697 | data->fb_vbitmap = fb_vbitmap; | ||
698 | data->fb_bitmap = fb_bitmap; | ||
699 | data->fb_bpp = picolcdfb_var.bits_per_pixel; | ||
700 | error = picolcd_fb_reset(data, 1); | ||
701 | if (error) { | ||
702 | dev_err(dev, "failed to configure display\n"); | ||
703 | goto err_cleanup; | ||
704 | } | ||
705 | error = device_create_file(dev, &dev_attr_fb_update_rate); | ||
706 | if (error) { | ||
707 | dev_err(dev, "failed to create sysfs attributes\n"); | ||
708 | goto err_cleanup; | ||
709 | } | ||
710 | data->fb_info = info; | ||
711 | error = register_framebuffer(info); | ||
712 | if (error) { | ||
713 | dev_err(dev, "failed to register framebuffer\n"); | ||
714 | goto err_sysfs; | ||
715 | } | ||
716 | fb_deferred_io_init(info); | ||
717 | /* schedule first output of framebuffer */ | ||
718 | schedule_delayed_work(&info->deferred_work, 0); | ||
719 | return 0; | ||
720 | |||
721 | err_sysfs: | ||
722 | device_remove_file(dev, &dev_attr_fb_update_rate); | ||
723 | err_cleanup: | ||
724 | data->fb_vbitmap = NULL; | ||
725 | data->fb_bitmap = NULL; | ||
726 | data->fb_bpp = 0; | ||
727 | data->fb_info = NULL; | ||
728 | |||
729 | err_nomem: | ||
730 | framebuffer_release(info); | ||
731 | vfree(fb_bitmap); | ||
732 | kfree(fb_vbitmap); | ||
733 | return error; | ||
734 | } | ||
735 | |||
736 | static void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
737 | { | ||
738 | struct fb_info *info = data->fb_info; | ||
739 | u8 *fb_vbitmap = data->fb_vbitmap; | ||
740 | u8 *fb_bitmap = data->fb_bitmap; | ||
741 | |||
742 | if (!info) | ||
743 | return; | ||
744 | |||
745 | data->fb_vbitmap = NULL; | ||
746 | data->fb_bitmap = NULL; | ||
747 | data->fb_bpp = 0; | ||
748 | data->fb_info = NULL; | ||
749 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | ||
750 | fb_deferred_io_cleanup(info); | ||
751 | unregister_framebuffer(info); | ||
752 | vfree(fb_bitmap); | ||
753 | kfree(fb_vbitmap); | ||
754 | } | ||
755 | |||
756 | #define picolcd_fbinfo(d) ((d)->fb_info) | ||
757 | #else | ||
758 | static inline int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
759 | { | ||
760 | return 0; | ||
761 | } | ||
762 | static inline int picolcd_init_framebuffer(struct picolcd_data *data) | ||
763 | { | ||
764 | return 0; | ||
765 | } | ||
766 | static void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
767 | { | ||
768 | } | ||
769 | #define picolcd_fbinfo(d) NULL | ||
770 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
771 | |||
772 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
773 | /* | ||
774 | * backlight class device | ||
775 | */ | ||
776 | static int picolcd_get_brightness(struct backlight_device *bdev) | ||
777 | { | ||
778 | struct picolcd_data *data = bl_get_data(bdev); | ||
779 | return data->lcd_brightness; | ||
780 | } | ||
781 | |||
782 | static int picolcd_set_brightness(struct backlight_device *bdev) | ||
783 | { | ||
784 | struct picolcd_data *data = bl_get_data(bdev); | ||
785 | struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev); | ||
786 | unsigned long flags; | ||
787 | |||
788 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
789 | return -ENODEV; | ||
790 | |||
791 | data->lcd_brightness = bdev->props.brightness & 0x0ff; | ||
792 | data->lcd_power = bdev->props.power; | ||
793 | spin_lock_irqsave(&data->lock, flags); | ||
794 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); | ||
795 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
796 | spin_unlock_irqrestore(&data->lock, flags); | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) | ||
801 | { | ||
802 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); | ||
803 | } | ||
804 | |||
805 | static const struct backlight_ops picolcd_blops = { | ||
806 | .update_status = picolcd_set_brightness, | ||
807 | .get_brightness = picolcd_get_brightness, | ||
808 | .check_fb = picolcd_check_bl_fb, | ||
809 | }; | ||
810 | |||
811 | static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) | ||
812 | { | ||
813 | struct device *dev = &data->hdev->dev; | ||
814 | struct backlight_device *bdev; | ||
815 | struct backlight_properties props; | ||
816 | if (!report) | ||
817 | return -ENODEV; | ||
818 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
819 | report->field[0]->report_size != 8) { | ||
820 | dev_err(dev, "unsupported BRIGHTNESS report"); | ||
821 | return -EINVAL; | ||
822 | } | ||
823 | |||
824 | memset(&props, 0, sizeof(props)); | ||
825 | props.max_brightness = 0xff; | ||
826 | bdev = backlight_device_register(dev_name(dev), dev, data, | ||
827 | &picolcd_blops, &props); | ||
828 | if (IS_ERR(bdev)) { | ||
829 | dev_err(dev, "failed to register backlight\n"); | ||
830 | return PTR_ERR(bdev); | ||
831 | } | ||
832 | bdev->props.brightness = 0xff; | ||
833 | data->lcd_brightness = 0xff; | ||
834 | data->backlight = bdev; | ||
835 | picolcd_set_brightness(bdev); | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | static void picolcd_exit_backlight(struct picolcd_data *data) | ||
840 | { | ||
841 | struct backlight_device *bdev = data->backlight; | ||
842 | |||
843 | data->backlight = NULL; | ||
844 | if (bdev) | ||
845 | backlight_device_unregister(bdev); | ||
846 | } | ||
847 | |||
848 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
849 | { | ||
850 | if (!data->backlight) | ||
851 | return 0; | ||
852 | return picolcd_set_brightness(data->backlight); | ||
853 | } | ||
854 | |||
855 | #else | ||
856 | static inline int picolcd_init_backlight(struct picolcd_data *data, | ||
857 | struct hid_report *report) | ||
858 | { | ||
859 | return 0; | ||
860 | } | ||
861 | static inline void picolcd_exit_backlight(struct picolcd_data *data) | ||
862 | { | ||
863 | } | ||
864 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
865 | { | ||
866 | return 0; | ||
867 | } | ||
868 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
869 | |||
870 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
871 | /* | ||
872 | * lcd class device | ||
873 | */ | ||
874 | static int picolcd_get_contrast(struct lcd_device *ldev) | ||
875 | { | ||
876 | struct picolcd_data *data = lcd_get_data(ldev); | ||
877 | return data->lcd_contrast; | ||
878 | } | ||
879 | |||
880 | static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) | ||
881 | { | ||
882 | struct picolcd_data *data = lcd_get_data(ldev); | ||
883 | struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev); | ||
884 | unsigned long flags; | ||
885 | |||
886 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
887 | return -ENODEV; | ||
888 | |||
889 | data->lcd_contrast = contrast & 0x0ff; | ||
890 | spin_lock_irqsave(&data->lock, flags); | ||
891 | hid_set_field(report->field[0], 0, data->lcd_contrast); | ||
892 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
893 | spin_unlock_irqrestore(&data->lock, flags); | ||
894 | return 0; | ||
895 | } | ||
896 | |||
897 | static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb) | ||
898 | { | ||
899 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev)); | ||
900 | } | ||
901 | |||
902 | static struct lcd_ops picolcd_lcdops = { | ||
903 | .get_contrast = picolcd_get_contrast, | ||
904 | .set_contrast = picolcd_set_contrast, | ||
905 | .check_fb = picolcd_check_lcd_fb, | ||
906 | }; | ||
907 | |||
908 | static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report) | ||
909 | { | ||
910 | struct device *dev = &data->hdev->dev; | ||
911 | struct lcd_device *ldev; | ||
912 | |||
913 | if (!report) | ||
914 | return -ENODEV; | ||
915 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
916 | report->field[0]->report_size != 8) { | ||
917 | dev_err(dev, "unsupported CONTRAST report"); | ||
918 | return -EINVAL; | ||
919 | } | ||
920 | |||
921 | ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops); | ||
922 | if (IS_ERR(ldev)) { | ||
923 | dev_err(dev, "failed to register LCD\n"); | ||
924 | return PTR_ERR(ldev); | ||
925 | } | ||
926 | ldev->props.max_contrast = 0x0ff; | ||
927 | data->lcd_contrast = 0xe5; | ||
928 | data->lcd = ldev; | ||
929 | picolcd_set_contrast(ldev, 0xe5); | ||
930 | return 0; | ||
931 | } | ||
932 | |||
933 | static void picolcd_exit_lcd(struct picolcd_data *data) | ||
934 | { | ||
935 | struct lcd_device *ldev = data->lcd; | ||
936 | |||
937 | data->lcd = NULL; | ||
938 | if (ldev) | ||
939 | lcd_device_unregister(ldev); | ||
940 | } | ||
941 | |||
942 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
943 | { | ||
944 | if (!data->lcd) | ||
945 | return 0; | ||
946 | return picolcd_set_contrast(data->lcd, data->lcd_contrast); | ||
947 | } | ||
948 | #else | ||
949 | static inline int picolcd_init_lcd(struct picolcd_data *data, | ||
950 | struct hid_report *report) | ||
951 | { | ||
952 | return 0; | ||
953 | } | ||
954 | static inline void picolcd_exit_lcd(struct picolcd_data *data) | ||
955 | { | ||
956 | } | ||
957 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
958 | { | ||
959 | return 0; | ||
960 | } | ||
961 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
962 | |||
963 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
964 | /** | ||
965 | * LED class device | ||
966 | */ | ||
967 | static void picolcd_leds_set(struct picolcd_data *data) | ||
968 | { | ||
969 | struct hid_report *report; | ||
970 | unsigned long flags; | ||
971 | |||
972 | if (!data->led[0]) | ||
973 | return; | ||
974 | report = picolcd_out_report(REPORT_LED_STATE, data->hdev); | ||
975 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
976 | return; | ||
977 | |||
978 | spin_lock_irqsave(&data->lock, flags); | ||
979 | hid_set_field(report->field[0], 0, data->led_state); | ||
980 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
981 | spin_unlock_irqrestore(&data->lock, flags); | ||
982 | } | ||
983 | |||
984 | static void picolcd_led_set_brightness(struct led_classdev *led_cdev, | ||
985 | enum led_brightness value) | ||
986 | { | ||
987 | struct device *dev; | ||
988 | struct hid_device *hdev; | ||
989 | struct picolcd_data *data; | ||
990 | int i, state = 0; | ||
991 | |||
992 | dev = led_cdev->dev->parent; | ||
993 | hdev = container_of(dev, struct hid_device, dev); | ||
994 | data = hid_get_drvdata(hdev); | ||
995 | for (i = 0; i < 8; i++) { | ||
996 | if (led_cdev != data->led[i]) | ||
997 | continue; | ||
998 | state = (data->led_state >> i) & 1; | ||
999 | if (value == LED_OFF && state) { | ||
1000 | data->led_state &= ~(1 << i); | ||
1001 | picolcd_leds_set(data); | ||
1002 | } else if (value != LED_OFF && !state) { | ||
1003 | data->led_state |= 1 << i; | ||
1004 | picolcd_leds_set(data); | ||
1005 | } | ||
1006 | break; | ||
1007 | } | ||
1008 | } | ||
1009 | |||
1010 | static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) | ||
1011 | { | ||
1012 | struct device *dev; | ||
1013 | struct hid_device *hdev; | ||
1014 | struct picolcd_data *data; | ||
1015 | int i, value = 0; | ||
1016 | |||
1017 | dev = led_cdev->dev->parent; | ||
1018 | hdev = container_of(dev, struct hid_device, dev); | ||
1019 | data = hid_get_drvdata(hdev); | ||
1020 | for (i = 0; i < 8; i++) | ||
1021 | if (led_cdev == data->led[i]) { | ||
1022 | value = (data->led_state >> i) & 1; | ||
1023 | break; | ||
1024 | } | ||
1025 | return value ? LED_FULL : LED_OFF; | ||
1026 | } | ||
1027 | |||
1028 | static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) | ||
1029 | { | ||
1030 | struct device *dev = &data->hdev->dev; | ||
1031 | struct led_classdev *led; | ||
1032 | size_t name_sz = strlen(dev_name(dev)) + 8; | ||
1033 | char *name; | ||
1034 | int i, ret = 0; | ||
1035 | |||
1036 | if (!report) | ||
1037 | return -ENODEV; | ||
1038 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
1039 | report->field[0]->report_size != 8) { | ||
1040 | dev_err(dev, "unsupported LED_STATE report"); | ||
1041 | return -EINVAL; | ||
1042 | } | ||
1043 | |||
1044 | for (i = 0; i < 8; i++) { | ||
1045 | led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); | ||
1046 | if (!led) { | ||
1047 | dev_err(dev, "can't allocate memory for LED %d\n", i); | ||
1048 | ret = -ENOMEM; | ||
1049 | goto err; | ||
1050 | } | ||
1051 | name = (void *)(&led[1]); | ||
1052 | snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); | ||
1053 | led->name = name; | ||
1054 | led->brightness = 0; | ||
1055 | led->max_brightness = 1; | ||
1056 | led->brightness_get = picolcd_led_get_brightness; | ||
1057 | led->brightness_set = picolcd_led_set_brightness; | ||
1058 | |||
1059 | data->led[i] = led; | ||
1060 | ret = led_classdev_register(dev, data->led[i]); | ||
1061 | if (ret) { | ||
1062 | data->led[i] = NULL; | ||
1063 | kfree(led); | ||
1064 | dev_err(dev, "can't register LED %d\n", i); | ||
1065 | goto err; | ||
1066 | } | ||
1067 | } | ||
1068 | return 0; | ||
1069 | err: | ||
1070 | for (i = 0; i < 8; i++) | ||
1071 | if (data->led[i]) { | ||
1072 | led = data->led[i]; | ||
1073 | data->led[i] = NULL; | ||
1074 | led_classdev_unregister(led); | ||
1075 | kfree(led); | ||
1076 | } | ||
1077 | return ret; | ||
1078 | } | ||
1079 | |||
1080 | static void picolcd_exit_leds(struct picolcd_data *data) | ||
1081 | { | ||
1082 | struct led_classdev *led; | ||
1083 | int i; | ||
1084 | |||
1085 | for (i = 0; i < 8; i++) { | ||
1086 | led = data->led[i]; | ||
1087 | data->led[i] = NULL; | ||
1088 | if (!led) | ||
1089 | continue; | ||
1090 | led_classdev_unregister(led); | ||
1091 | kfree(led); | ||
1092 | } | ||
1093 | } | ||
1094 | |||
1095 | #else | ||
1096 | static inline int picolcd_init_leds(struct picolcd_data *data, | ||
1097 | struct hid_report *report) | ||
1098 | { | ||
1099 | return 0; | ||
1100 | } | ||
1101 | static void picolcd_exit_leds(struct picolcd_data *data) | ||
1102 | { | ||
1103 | } | ||
1104 | static inline int picolcd_leds_set(struct picolcd_data *data) | ||
1105 | { | ||
1106 | return 0; | ||
1107 | } | ||
1108 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
1109 | |||
1110 | /* | ||
1111 | * input class device | ||
1112 | */ | ||
1113 | static int picolcd_raw_keypad(struct picolcd_data *data, | ||
1114 | struct hid_report *report, u8 *raw_data, int size) | ||
1115 | { | ||
1116 | /* | ||
1117 | * Keypad event | ||
1118 | * First and second data bytes list currently pressed keys, | ||
1119 | * 0x00 means no key and at most 2 keys may be pressed at same time | ||
1120 | */ | ||
1121 | int i, j; | ||
1122 | |||
1123 | /* determine newly pressed keys */ | ||
1124 | for (i = 0; i < size; i++) { | ||
1125 | unsigned int key_code; | ||
1126 | if (raw_data[i] == 0) | ||
1127 | continue; | ||
1128 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
1129 | if (data->pressed_keys[j] == raw_data[i]) | ||
1130 | goto key_already_down; | ||
1131 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
1132 | if (data->pressed_keys[j] == 0) { | ||
1133 | data->pressed_keys[j] = raw_data[i]; | ||
1134 | break; | ||
1135 | } | ||
1136 | input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]); | ||
1137 | if (raw_data[i] < PICOLCD_KEYS) | ||
1138 | key_code = data->keycode[raw_data[i]]; | ||
1139 | else | ||
1140 | key_code = KEY_UNKNOWN; | ||
1141 | if (key_code != KEY_UNKNOWN) { | ||
1142 | dbg_hid(PICOLCD_NAME " got key press for %u:%d", | ||
1143 | raw_data[i], key_code); | ||
1144 | input_report_key(data->input_keys, key_code, 1); | ||
1145 | } | ||
1146 | input_sync(data->input_keys); | ||
1147 | key_already_down: | ||
1148 | continue; | ||
1149 | } | ||
1150 | |||
1151 | /* determine newly released keys */ | ||
1152 | for (j = 0; j < sizeof(data->pressed_keys); j++) { | ||
1153 | unsigned int key_code; | ||
1154 | if (data->pressed_keys[j] == 0) | ||
1155 | continue; | ||
1156 | for (i = 0; i < size; i++) | ||
1157 | if (data->pressed_keys[j] == raw_data[i]) | ||
1158 | goto key_still_down; | ||
1159 | input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]); | ||
1160 | if (data->pressed_keys[j] < PICOLCD_KEYS) | ||
1161 | key_code = data->keycode[data->pressed_keys[j]]; | ||
1162 | else | ||
1163 | key_code = KEY_UNKNOWN; | ||
1164 | if (key_code != KEY_UNKNOWN) { | ||
1165 | dbg_hid(PICOLCD_NAME " got key release for %u:%d", | ||
1166 | data->pressed_keys[j], key_code); | ||
1167 | input_report_key(data->input_keys, key_code, 0); | ||
1168 | } | ||
1169 | input_sync(data->input_keys); | ||
1170 | data->pressed_keys[j] = 0; | ||
1171 | key_still_down: | ||
1172 | continue; | ||
1173 | } | ||
1174 | return 1; | ||
1175 | } | ||
1176 | |||
1177 | static int picolcd_raw_cir(struct picolcd_data *data, | ||
1178 | struct hid_report *report, u8 *raw_data, int size) | ||
1179 | { | ||
1180 | /* Need understanding of CIR data format to implement ... */ | ||
1181 | return 1; | ||
1182 | } | ||
1183 | |||
1184 | static int picolcd_check_version(struct hid_device *hdev) | ||
1185 | { | ||
1186 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
1187 | struct picolcd_pending *verinfo; | ||
1188 | int ret = 0; | ||
1189 | |||
1190 | if (!data) | ||
1191 | return -ENODEV; | ||
1192 | |||
1193 | verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0); | ||
1194 | if (!verinfo) { | ||
1195 | dev_err(&hdev->dev, "no version response from PicoLCD"); | ||
1196 | return -ENODEV; | ||
1197 | } | ||
1198 | |||
1199 | if (verinfo->raw_size == 2) { | ||
1200 | data->version[0] = verinfo->raw_data[1]; | ||
1201 | data->version[1] = verinfo->raw_data[0]; | ||
1202 | if (data->status & PICOLCD_BOOTLOADER) { | ||
1203 | dev_info(&hdev->dev, "PicoLCD, bootloader version %d.%d\n", | ||
1204 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
1205 | } else { | ||
1206 | dev_info(&hdev->dev, "PicoLCD, firmware version %d.%d\n", | ||
1207 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
1208 | } | ||
1209 | } else { | ||
1210 | dev_err(&hdev->dev, "confused, got unexpected version response from PicoLCD\n"); | ||
1211 | ret = -EINVAL; | ||
1212 | } | ||
1213 | kfree(verinfo); | ||
1214 | return ret; | ||
1215 | } | ||
1216 | |||
1217 | /* | ||
1218 | * Reset our device and wait for answer to VERSION request | ||
1219 | */ | ||
1220 | static int picolcd_reset(struct hid_device *hdev) | ||
1221 | { | ||
1222 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
1223 | struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); | ||
1224 | unsigned long flags; | ||
1225 | int error; | ||
1226 | |||
1227 | if (!data || !report || report->maxfield != 1) | ||
1228 | return -ENODEV; | ||
1229 | |||
1230 | spin_lock_irqsave(&data->lock, flags); | ||
1231 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
1232 | data->status |= PICOLCD_BOOTLOADER; | ||
1233 | |||
1234 | /* perform the reset */ | ||
1235 | hid_set_field(report->field[0], 0, 1); | ||
1236 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
1237 | spin_unlock_irqrestore(&data->lock, flags); | ||
1238 | |||
1239 | error = picolcd_check_version(hdev); | ||
1240 | if (error) | ||
1241 | return error; | ||
1242 | |||
1243 | picolcd_resume_lcd(data); | ||
1244 | picolcd_resume_backlight(data); | ||
1245 | #ifdef CONFIG_HID_PICOLCD_FB | ||
1246 | if (data->fb_info) | ||
1247 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
1248 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
1249 | |||
1250 | picolcd_leds_set(data); | ||
1251 | return 0; | ||
1252 | } | ||
1253 | |||
1254 | /* | ||
1255 | * The "operation_mode" sysfs attribute | ||
1256 | */ | ||
1257 | static ssize_t picolcd_operation_mode_show(struct device *dev, | ||
1258 | struct device_attribute *attr, char *buf) | ||
1259 | { | ||
1260 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
1261 | |||
1262 | if (data->status & PICOLCD_BOOTLOADER) | ||
1263 | return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n"); | ||
1264 | else | ||
1265 | return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n"); | ||
1266 | } | ||
1267 | |||
1268 | static ssize_t picolcd_operation_mode_store(struct device *dev, | ||
1269 | struct device_attribute *attr, const char *buf, size_t count) | ||
1270 | { | ||
1271 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
1272 | struct hid_report *report = NULL; | ||
1273 | size_t cnt = count; | ||
1274 | int timeout = data->opmode_delay; | ||
1275 | unsigned long flags; | ||
1276 | |||
1277 | if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) { | ||
1278 | if (data->status & PICOLCD_BOOTLOADER) | ||
1279 | report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev); | ||
1280 | buf += 3; | ||
1281 | cnt -= 3; | ||
1282 | } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) { | ||
1283 | if (!(data->status & PICOLCD_BOOTLOADER)) | ||
1284 | report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev); | ||
1285 | buf += 10; | ||
1286 | cnt -= 10; | ||
1287 | } | ||
1288 | if (!report) | ||
1289 | return -EINVAL; | ||
1290 | |||
1291 | while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) | ||
1292 | cnt--; | ||
1293 | if (cnt != 0) | ||
1294 | return -EINVAL; | ||
1295 | |||
1296 | spin_lock_irqsave(&data->lock, flags); | ||
1297 | hid_set_field(report->field[0], 0, timeout & 0xff); | ||
1298 | hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); | ||
1299 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
1300 | spin_unlock_irqrestore(&data->lock, flags); | ||
1301 | return count; | ||
1302 | } | ||
1303 | |||
1304 | static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show, | ||
1305 | picolcd_operation_mode_store); | ||
1306 | |||
1307 | /* | ||
1308 | * The "operation_mode_delay" sysfs attribute | ||
1309 | */ | ||
1310 | static ssize_t picolcd_operation_mode_delay_show(struct device *dev, | ||
1311 | struct device_attribute *attr, char *buf) | ||
1312 | { | ||
1313 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
1314 | |||
1315 | return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay); | ||
1316 | } | ||
1317 | |||
1318 | static ssize_t picolcd_operation_mode_delay_store(struct device *dev, | ||
1319 | struct device_attribute *attr, const char *buf, size_t count) | ||
1320 | { | ||
1321 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
1322 | unsigned u; | ||
1323 | if (sscanf(buf, "%u", &u) != 1) | ||
1324 | return -EINVAL; | ||
1325 | if (u > 30000) | ||
1326 | return -EINVAL; | ||
1327 | else | ||
1328 | data->opmode_delay = u; | ||
1329 | return count; | ||
1330 | } | ||
1331 | |||
1332 | static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show, | ||
1333 | picolcd_operation_mode_delay_store); | ||
1334 | |||
1335 | |||
1336 | #ifdef CONFIG_DEBUG_FS | ||
1337 | /* | ||
1338 | * The "reset" file | ||
1339 | */ | ||
1340 | static int picolcd_debug_reset_show(struct seq_file *f, void *p) | ||
1341 | { | ||
1342 | if (picolcd_fbinfo((struct picolcd_data *)f->private)) | ||
1343 | seq_printf(f, "all fb\n"); | ||
1344 | else | ||
1345 | seq_printf(f, "all\n"); | ||
1346 | return 0; | ||
1347 | } | ||
1348 | |||
1349 | static int picolcd_debug_reset_open(struct inode *inode, struct file *f) | ||
1350 | { | ||
1351 | return single_open(f, picolcd_debug_reset_show, inode->i_private); | ||
1352 | } | ||
1353 | |||
1354 | static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf, | ||
1355 | size_t count, loff_t *ppos) | ||
1356 | { | ||
1357 | struct picolcd_data *data = ((struct seq_file *)f->private_data)->private; | ||
1358 | char buf[32]; | ||
1359 | size_t cnt = min(count, sizeof(buf)-1); | ||
1360 | if (copy_from_user(buf, user_buf, cnt)) | ||
1361 | return -EFAULT; | ||
1362 | |||
1363 | while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n')) | ||
1364 | cnt--; | ||
1365 | buf[cnt] = '\0'; | ||
1366 | if (strcmp(buf, "all") == 0) { | ||
1367 | picolcd_reset(data->hdev); | ||
1368 | picolcd_fb_reset(data, 1); | ||
1369 | } else if (strcmp(buf, "fb") == 0) { | ||
1370 | picolcd_fb_reset(data, 1); | ||
1371 | } else { | ||
1372 | return -EINVAL; | ||
1373 | } | ||
1374 | return count; | ||
1375 | } | ||
1376 | |||
1377 | static const struct file_operations picolcd_debug_reset_fops = { | ||
1378 | .owner = THIS_MODULE, | ||
1379 | .open = picolcd_debug_reset_open, | ||
1380 | .read = seq_read, | ||
1381 | .llseek = seq_lseek, | ||
1382 | .write = picolcd_debug_reset_write, | ||
1383 | .release = single_release, | ||
1384 | }; | ||
1385 | |||
1386 | /* | ||
1387 | * The "eeprom" file | ||
1388 | */ | ||
1389 | static int picolcd_debug_eeprom_open(struct inode *i, struct file *f) | ||
1390 | { | ||
1391 | f->private_data = i->i_private; | ||
1392 | return 0; | ||
1393 | } | ||
1394 | |||
1395 | static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, | ||
1396 | size_t s, loff_t *off) | ||
1397 | { | ||
1398 | struct picolcd_data *data = f->private_data; | ||
1399 | struct picolcd_pending *resp; | ||
1400 | u8 raw_data[3]; | ||
1401 | ssize_t ret = -EIO; | ||
1402 | |||
1403 | if (s == 0) | ||
1404 | return -EINVAL; | ||
1405 | if (*off > 0x0ff) | ||
1406 | return 0; | ||
1407 | |||
1408 | /* prepare buffer with info about what we want to read (addr & len) */ | ||
1409 | raw_data[0] = *off & 0xff; | ||
1410 | raw_data[1] = (*off >> 8) && 0xff; | ||
1411 | raw_data[2] = s < 20 ? s : 20; | ||
1412 | if (*off + raw_data[2] > 0xff) | ||
1413 | raw_data[2] = 0x100 - *off; | ||
1414 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data, | ||
1415 | sizeof(raw_data)); | ||
1416 | if (!resp) | ||
1417 | return -EIO; | ||
1418 | |||
1419 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
1420 | /* successful read :) */ | ||
1421 | ret = resp->raw_data[2]; | ||
1422 | if (ret > s) | ||
1423 | ret = s; | ||
1424 | if (copy_to_user(u, resp->raw_data+3, ret)) | ||
1425 | ret = -EFAULT; | ||
1426 | else | ||
1427 | *off += ret; | ||
1428 | } /* anything else is some kind of IO error */ | ||
1429 | |||
1430 | kfree(resp); | ||
1431 | return ret; | ||
1432 | } | ||
1433 | |||
1434 | static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, | ||
1435 | size_t s, loff_t *off) | ||
1436 | { | ||
1437 | struct picolcd_data *data = f->private_data; | ||
1438 | struct picolcd_pending *resp; | ||
1439 | ssize_t ret = -EIO; | ||
1440 | u8 raw_data[23]; | ||
1441 | |||
1442 | if (s == 0) | ||
1443 | return -EINVAL; | ||
1444 | if (*off > 0x0ff) | ||
1445 | return -ENOSPC; | ||
1446 | |||
1447 | memset(raw_data, 0, sizeof(raw_data)); | ||
1448 | raw_data[0] = *off & 0xff; | ||
1449 | raw_data[1] = (*off >> 8) && 0xff; | ||
1450 | raw_data[2] = s < 20 ? s : 20; | ||
1451 | if (*off + raw_data[2] > 0xff) | ||
1452 | raw_data[2] = 0x100 - *off; | ||
1453 | |||
1454 | if (copy_from_user(raw_data+3, u, raw_data[2])) | ||
1455 | return -EFAULT; | ||
1456 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data, | ||
1457 | sizeof(raw_data)); | ||
1458 | |||
1459 | if (!resp) | ||
1460 | return -EIO; | ||
1461 | |||
1462 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
1463 | /* check if written data matches */ | ||
1464 | if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) { | ||
1465 | *off += raw_data[2]; | ||
1466 | ret = raw_data[2]; | ||
1467 | } | ||
1468 | } | ||
1469 | kfree(resp); | ||
1470 | return ret; | ||
1471 | } | ||
1472 | |||
1473 | /* | ||
1474 | * Notes: | ||
1475 | * - read/write happens in chunks of at most 20 bytes, it's up to userspace | ||
1476 | * to loop in order to get more data. | ||
1477 | * - on write errors on otherwise correct write request the bytes | ||
1478 | * that should have been written are in undefined state. | ||
1479 | */ | ||
1480 | static const struct file_operations picolcd_debug_eeprom_fops = { | ||
1481 | .owner = THIS_MODULE, | ||
1482 | .open = picolcd_debug_eeprom_open, | ||
1483 | .read = picolcd_debug_eeprom_read, | ||
1484 | .write = picolcd_debug_eeprom_write, | ||
1485 | .llseek = generic_file_llseek, | ||
1486 | }; | ||
1487 | |||
1488 | /* | ||
1489 | * The "flash" file | ||
1490 | */ | ||
1491 | static int picolcd_debug_flash_open(struct inode *i, struct file *f) | ||
1492 | { | ||
1493 | f->private_data = i->i_private; | ||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1497 | /* record a flash address to buf (bounds check to be done by caller) */ | ||
1498 | static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) | ||
1499 | { | ||
1500 | buf[0] = off & 0xff; | ||
1501 | buf[1] = (off >> 8) & 0xff; | ||
1502 | if (data->addr_sz == 3) | ||
1503 | buf[2] = (off >> 16) & 0xff; | ||
1504 | return data->addr_sz == 2 ? 2 : 3; | ||
1505 | } | ||
1506 | |||
1507 | /* read a given size of data (bounds check to be done by caller) */ | ||
1508 | static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id, | ||
1509 | char __user *u, size_t s, loff_t *off) | ||
1510 | { | ||
1511 | struct picolcd_pending *resp; | ||
1512 | u8 raw_data[4]; | ||
1513 | ssize_t ret = 0; | ||
1514 | int len_off, err = -EIO; | ||
1515 | |||
1516 | while (s > 0) { | ||
1517 | err = -EIO; | ||
1518 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
1519 | raw_data[len_off] = s > 32 ? 32 : s; | ||
1520 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1); | ||
1521 | if (!resp || !resp->in_report) | ||
1522 | goto skip; | ||
1523 | if (resp->in_report->id == REPORT_MEMORY || | ||
1524 | resp->in_report->id == REPORT_BL_READ_MEMORY) { | ||
1525 | if (memcmp(raw_data, resp->raw_data, len_off+1) != 0) | ||
1526 | goto skip; | ||
1527 | if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) { | ||
1528 | err = -EFAULT; | ||
1529 | goto skip; | ||
1530 | } | ||
1531 | *off += raw_data[len_off]; | ||
1532 | s -= raw_data[len_off]; | ||
1533 | ret += raw_data[len_off]; | ||
1534 | err = 0; | ||
1535 | } | ||
1536 | skip: | ||
1537 | kfree(resp); | ||
1538 | if (err) | ||
1539 | return ret > 0 ? ret : err; | ||
1540 | } | ||
1541 | return ret; | ||
1542 | } | ||
1543 | |||
1544 | static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u, | ||
1545 | size_t s, loff_t *off) | ||
1546 | { | ||
1547 | struct picolcd_data *data = f->private_data; | ||
1548 | |||
1549 | if (s == 0) | ||
1550 | return -EINVAL; | ||
1551 | if (*off > 0x05fff) | ||
1552 | return 0; | ||
1553 | if (*off + s > 0x05fff) | ||
1554 | s = 0x06000 - *off; | ||
1555 | |||
1556 | if (data->status & PICOLCD_BOOTLOADER) | ||
1557 | return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off); | ||
1558 | else | ||
1559 | return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off); | ||
1560 | } | ||
1561 | |||
1562 | /* erase block aligned to 64bytes boundary */ | ||
1563 | static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id, | ||
1564 | loff_t *off) | ||
1565 | { | ||
1566 | struct picolcd_pending *resp; | ||
1567 | u8 raw_data[3]; | ||
1568 | int len_off; | ||
1569 | ssize_t ret = -EIO; | ||
1570 | |||
1571 | if (*off & 0x3f) | ||
1572 | return -EINVAL; | ||
1573 | |||
1574 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
1575 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off); | ||
1576 | if (!resp || !resp->in_report) | ||
1577 | goto skip; | ||
1578 | if (resp->in_report->id == REPORT_MEMORY || | ||
1579 | resp->in_report->id == REPORT_BL_ERASE_MEMORY) { | ||
1580 | if (memcmp(raw_data, resp->raw_data, len_off) != 0) | ||
1581 | goto skip; | ||
1582 | ret = 0; | ||
1583 | } | ||
1584 | skip: | ||
1585 | kfree(resp); | ||
1586 | return ret; | ||
1587 | } | ||
1588 | |||
1589 | /* write a given size of data (bounds check to be done by caller) */ | ||
1590 | static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id, | ||
1591 | const char __user *u, size_t s, loff_t *off) | ||
1592 | { | ||
1593 | struct picolcd_pending *resp; | ||
1594 | u8 raw_data[36]; | ||
1595 | ssize_t ret = 0; | ||
1596 | int len_off, err = -EIO; | ||
1597 | |||
1598 | while (s > 0) { | ||
1599 | err = -EIO; | ||
1600 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
1601 | raw_data[len_off] = s > 32 ? 32 : s; | ||
1602 | if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) { | ||
1603 | err = -EFAULT; | ||
1604 | goto skip; | ||
1605 | } | ||
1606 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, | ||
1607 | len_off+1+raw_data[len_off]); | ||
1608 | if (!resp || !resp->in_report) | ||
1609 | goto skip; | ||
1610 | if (resp->in_report->id == REPORT_MEMORY || | ||
1611 | resp->in_report->id == REPORT_BL_WRITE_MEMORY) { | ||
1612 | if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0) | ||
1613 | goto skip; | ||
1614 | *off += raw_data[len_off]; | ||
1615 | s -= raw_data[len_off]; | ||
1616 | ret += raw_data[len_off]; | ||
1617 | err = 0; | ||
1618 | } | ||
1619 | skip: | ||
1620 | kfree(resp); | ||
1621 | if (err) | ||
1622 | break; | ||
1623 | } | ||
1624 | return ret > 0 ? ret : err; | ||
1625 | } | ||
1626 | |||
1627 | static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, | ||
1628 | size_t s, loff_t *off) | ||
1629 | { | ||
1630 | struct picolcd_data *data = f->private_data; | ||
1631 | ssize_t err, ret = 0; | ||
1632 | int report_erase, report_write; | ||
1633 | |||
1634 | if (s == 0) | ||
1635 | return -EINVAL; | ||
1636 | if (*off > 0x5fff) | ||
1637 | return -ENOSPC; | ||
1638 | if (s & 0x3f) | ||
1639 | return -EINVAL; | ||
1640 | if (*off & 0x3f) | ||
1641 | return -EINVAL; | ||
1642 | |||
1643 | if (data->status & PICOLCD_BOOTLOADER) { | ||
1644 | report_erase = REPORT_BL_ERASE_MEMORY; | ||
1645 | report_write = REPORT_BL_WRITE_MEMORY; | ||
1646 | } else { | ||
1647 | report_erase = REPORT_ERASE_MEMORY; | ||
1648 | report_write = REPORT_WRITE_MEMORY; | ||
1649 | } | ||
1650 | mutex_lock(&data->mutex_flash); | ||
1651 | while (s > 0) { | ||
1652 | err = _picolcd_flash_erase64(data, report_erase, off); | ||
1653 | if (err) | ||
1654 | break; | ||
1655 | err = _picolcd_flash_write(data, report_write, u, 64, off); | ||
1656 | if (err < 0) | ||
1657 | break; | ||
1658 | ret += err; | ||
1659 | *off += err; | ||
1660 | s -= err; | ||
1661 | if (err != 64) | ||
1662 | break; | ||
1663 | } | ||
1664 | mutex_unlock(&data->mutex_flash); | ||
1665 | return ret > 0 ? ret : err; | ||
1666 | } | ||
1667 | |||
1668 | /* | ||
1669 | * Notes: | ||
1670 | * - concurrent writing is prevented by mutex and all writes must be | ||
1671 | * n*64 bytes and 64-byte aligned, each write being preceeded by an | ||
1672 | * ERASE which erases a 64byte block. | ||
1673 | * If less than requested was written or an error is returned for an | ||
1674 | * otherwise correct write request the next 64-byte block which should | ||
1675 | * have been written is in undefined state (mostly: original, erased, | ||
1676 | * (half-)written with write error) | ||
1677 | * - reading can happend without special restriction | ||
1678 | */ | ||
1679 | static const struct file_operations picolcd_debug_flash_fops = { | ||
1680 | .owner = THIS_MODULE, | ||
1681 | .open = picolcd_debug_flash_open, | ||
1682 | .read = picolcd_debug_flash_read, | ||
1683 | .write = picolcd_debug_flash_write, | ||
1684 | .llseek = generic_file_llseek, | ||
1685 | }; | ||
1686 | |||
1687 | |||
1688 | /* | ||
1689 | * Helper code for HID report level dumping/debugging | ||
1690 | */ | ||
1691 | static const char *error_codes[] = { | ||
1692 | "success", "parameter missing", "data_missing", "block readonly", | ||
1693 | "block not erasable", "block too big", "section overflow", | ||
1694 | "invalid command length", "invalid data length", | ||
1695 | }; | ||
1696 | |||
1697 | static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, | ||
1698 | const size_t data_len) | ||
1699 | { | ||
1700 | int i, j; | ||
1701 | for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) { | ||
1702 | dst[j++] = hex_asc[(data[i] >> 4) & 0x0f]; | ||
1703 | dst[j++] = hex_asc[data[i] & 0x0f]; | ||
1704 | dst[j++] = ' '; | ||
1705 | } | ||
1706 | if (j < dst_sz) { | ||
1707 | dst[j--] = '\0'; | ||
1708 | dst[j] = '\n'; | ||
1709 | } else | ||
1710 | dst[j] = '\0'; | ||
1711 | } | ||
1712 | |||
1713 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
1714 | struct hid_device *hdev, struct hid_report *report) | ||
1715 | { | ||
1716 | u8 raw_data[70]; | ||
1717 | int raw_size = (report->size >> 3) + 1; | ||
1718 | char *buff; | ||
1719 | #define BUFF_SZ 256 | ||
1720 | |||
1721 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
1722 | if (!hdev->debug_events) | ||
1723 | return; | ||
1724 | |||
1725 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
1726 | if (!buff) | ||
1727 | return; | ||
1728 | |||
1729 | snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", | ||
1730 | report->id, raw_size); | ||
1731 | hid_debug_event(hdev, buff); | ||
1732 | if (raw_size + 5 > sizeof(raw_data)) { | ||
1733 | hid_debug_event(hdev, " TOO BIG\n"); | ||
1734 | return; | ||
1735 | } else { | ||
1736 | raw_data[0] = report->id; | ||
1737 | hid_output_report(report, raw_data); | ||
1738 | dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); | ||
1739 | hid_debug_event(hdev, buff); | ||
1740 | } | ||
1741 | |||
1742 | switch (report->id) { | ||
1743 | case REPORT_LED_STATE: | ||
1744 | /* 1 data byte with GPO state */ | ||
1745 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1746 | "REPORT_LED_STATE", report->id, raw_size-1); | ||
1747 | hid_debug_event(hdev, buff); | ||
1748 | snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]); | ||
1749 | hid_debug_event(hdev, buff); | ||
1750 | break; | ||
1751 | case REPORT_BRIGHTNESS: | ||
1752 | /* 1 data byte with brightness */ | ||
1753 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1754 | "REPORT_BRIGHTNESS", report->id, raw_size-1); | ||
1755 | hid_debug_event(hdev, buff); | ||
1756 | snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]); | ||
1757 | hid_debug_event(hdev, buff); | ||
1758 | break; | ||
1759 | case REPORT_CONTRAST: | ||
1760 | /* 1 data byte with contrast */ | ||
1761 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1762 | "REPORT_CONTRAST", report->id, raw_size-1); | ||
1763 | hid_debug_event(hdev, buff); | ||
1764 | snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]); | ||
1765 | hid_debug_event(hdev, buff); | ||
1766 | break; | ||
1767 | case REPORT_RESET: | ||
1768 | /* 2 data bytes with reset duration in ms */ | ||
1769 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1770 | "REPORT_RESET", report->id, raw_size-1); | ||
1771 | hid_debug_event(hdev, buff); | ||
1772 | snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n", | ||
1773 | raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]); | ||
1774 | hid_debug_event(hdev, buff); | ||
1775 | break; | ||
1776 | case REPORT_LCD_CMD: | ||
1777 | /* 63 data bytes with LCD commands */ | ||
1778 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1779 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
1780 | hid_debug_event(hdev, buff); | ||
1781 | /* TODO: format decoding */ | ||
1782 | break; | ||
1783 | case REPORT_LCD_DATA: | ||
1784 | /* 63 data bytes with LCD data */ | ||
1785 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1786 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
1787 | /* TODO: format decoding */ | ||
1788 | hid_debug_event(hdev, buff); | ||
1789 | break; | ||
1790 | case REPORT_LCD_CMD_DATA: | ||
1791 | /* 63 data bytes with LCD commands and data */ | ||
1792 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1793 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
1794 | /* TODO: format decoding */ | ||
1795 | hid_debug_event(hdev, buff); | ||
1796 | break; | ||
1797 | case REPORT_EE_READ: | ||
1798 | /* 3 data bytes with read area description */ | ||
1799 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1800 | "REPORT_EE_READ", report->id, raw_size-1); | ||
1801 | hid_debug_event(hdev, buff); | ||
1802 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
1803 | raw_data[2], raw_data[1]); | ||
1804 | hid_debug_event(hdev, buff); | ||
1805 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
1806 | hid_debug_event(hdev, buff); | ||
1807 | break; | ||
1808 | case REPORT_EE_WRITE: | ||
1809 | /* 3+1..20 data bytes with write area description */ | ||
1810 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1811 | "REPORT_EE_WRITE", report->id, raw_size-1); | ||
1812 | hid_debug_event(hdev, buff); | ||
1813 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
1814 | raw_data[2], raw_data[1]); | ||
1815 | hid_debug_event(hdev, buff); | ||
1816 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
1817 | hid_debug_event(hdev, buff); | ||
1818 | if (raw_data[3] == 0) { | ||
1819 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
1820 | } else if (raw_data[3] + 4 <= raw_size) { | ||
1821 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
1822 | hid_debug_event(hdev, buff); | ||
1823 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
1824 | } else { | ||
1825 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
1826 | } | ||
1827 | hid_debug_event(hdev, buff); | ||
1828 | break; | ||
1829 | case REPORT_ERASE_MEMORY: | ||
1830 | case REPORT_BL_ERASE_MEMORY: | ||
1831 | /* 3 data bytes with pointer inside erase block */ | ||
1832 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1833 | "REPORT_ERASE_MEMORY", report->id, raw_size-1); | ||
1834 | hid_debug_event(hdev, buff); | ||
1835 | switch (data->addr_sz) { | ||
1836 | case 2: | ||
1837 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n", | ||
1838 | raw_data[2], raw_data[1]); | ||
1839 | break; | ||
1840 | case 3: | ||
1841 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n", | ||
1842 | raw_data[3], raw_data[2], raw_data[1]); | ||
1843 | break; | ||
1844 | default: | ||
1845 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
1846 | } | ||
1847 | hid_debug_event(hdev, buff); | ||
1848 | break; | ||
1849 | case REPORT_READ_MEMORY: | ||
1850 | case REPORT_BL_READ_MEMORY: | ||
1851 | /* 4 data bytes with read area description */ | ||
1852 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1853 | "REPORT_READ_MEMORY", report->id, raw_size-1); | ||
1854 | hid_debug_event(hdev, buff); | ||
1855 | switch (data->addr_sz) { | ||
1856 | case 2: | ||
1857 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
1858 | raw_data[2], raw_data[1]); | ||
1859 | hid_debug_event(hdev, buff); | ||
1860 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
1861 | break; | ||
1862 | case 3: | ||
1863 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
1864 | raw_data[3], raw_data[2], raw_data[1]); | ||
1865 | hid_debug_event(hdev, buff); | ||
1866 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
1867 | break; | ||
1868 | default: | ||
1869 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
1870 | } | ||
1871 | hid_debug_event(hdev, buff); | ||
1872 | break; | ||
1873 | case REPORT_WRITE_MEMORY: | ||
1874 | case REPORT_BL_WRITE_MEMORY: | ||
1875 | /* 4+1..32 data bytes with write adrea description */ | ||
1876 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1877 | "REPORT_WRITE_MEMORY", report->id, raw_size-1); | ||
1878 | hid_debug_event(hdev, buff); | ||
1879 | switch (data->addr_sz) { | ||
1880 | case 2: | ||
1881 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
1882 | raw_data[2], raw_data[1]); | ||
1883 | hid_debug_event(hdev, buff); | ||
1884 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
1885 | hid_debug_event(hdev, buff); | ||
1886 | if (raw_data[3] == 0) { | ||
1887 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
1888 | } else if (raw_data[3] + 4 <= raw_size) { | ||
1889 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
1890 | hid_debug_event(hdev, buff); | ||
1891 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
1892 | } else { | ||
1893 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
1894 | } | ||
1895 | break; | ||
1896 | case 3: | ||
1897 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
1898 | raw_data[3], raw_data[2], raw_data[1]); | ||
1899 | hid_debug_event(hdev, buff); | ||
1900 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
1901 | hid_debug_event(hdev, buff); | ||
1902 | if (raw_data[4] == 0) { | ||
1903 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
1904 | } else if (raw_data[4] + 5 <= raw_size) { | ||
1905 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
1906 | hid_debug_event(hdev, buff); | ||
1907 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
1908 | } else { | ||
1909 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
1910 | } | ||
1911 | break; | ||
1912 | default: | ||
1913 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
1914 | } | ||
1915 | hid_debug_event(hdev, buff); | ||
1916 | break; | ||
1917 | case REPORT_SPLASH_RESTART: | ||
1918 | /* TODO */ | ||
1919 | break; | ||
1920 | case REPORT_EXIT_KEYBOARD: | ||
1921 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1922 | "REPORT_EXIT_KEYBOARD", report->id, raw_size-1); | ||
1923 | hid_debug_event(hdev, buff); | ||
1924 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
1925 | raw_data[1] | (raw_data[2] << 8), | ||
1926 | raw_data[2], raw_data[1]); | ||
1927 | hid_debug_event(hdev, buff); | ||
1928 | break; | ||
1929 | case REPORT_VERSION: | ||
1930 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1931 | "REPORT_VERSION", report->id, raw_size-1); | ||
1932 | hid_debug_event(hdev, buff); | ||
1933 | break; | ||
1934 | case REPORT_DEVID: | ||
1935 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1936 | "REPORT_DEVID", report->id, raw_size-1); | ||
1937 | hid_debug_event(hdev, buff); | ||
1938 | break; | ||
1939 | case REPORT_SPLASH_SIZE: | ||
1940 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1941 | "REPORT_SPLASH_SIZE", report->id, raw_size-1); | ||
1942 | hid_debug_event(hdev, buff); | ||
1943 | break; | ||
1944 | case REPORT_HOOK_VERSION: | ||
1945 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1946 | "REPORT_HOOK_VERSION", report->id, raw_size-1); | ||
1947 | hid_debug_event(hdev, buff); | ||
1948 | break; | ||
1949 | case REPORT_EXIT_FLASHER: | ||
1950 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1951 | "REPORT_VERSION", report->id, raw_size-1); | ||
1952 | hid_debug_event(hdev, buff); | ||
1953 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
1954 | raw_data[1] | (raw_data[2] << 8), | ||
1955 | raw_data[2], raw_data[1]); | ||
1956 | hid_debug_event(hdev, buff); | ||
1957 | break; | ||
1958 | default: | ||
1959 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
1960 | "<unknown>", report->id, raw_size-1); | ||
1961 | hid_debug_event(hdev, buff); | ||
1962 | break; | ||
1963 | } | ||
1964 | wake_up_interruptible(&hdev->debug_wait); | ||
1965 | kfree(buff); | ||
1966 | } | ||
1967 | |||
1968 | static void picolcd_debug_raw_event(struct picolcd_data *data, | ||
1969 | struct hid_device *hdev, struct hid_report *report, | ||
1970 | u8 *raw_data, int size) | ||
1971 | { | ||
1972 | char *buff; | ||
1973 | |||
1974 | #define BUFF_SZ 256 | ||
1975 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
1976 | if (!hdev->debug_events) | ||
1977 | return; | ||
1978 | |||
1979 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
1980 | if (!buff) | ||
1981 | return; | ||
1982 | |||
1983 | switch (report->id) { | ||
1984 | case REPORT_ERROR_CODE: | ||
1985 | /* 2 data bytes with affected report and error code */ | ||
1986 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
1987 | "REPORT_ERROR_CODE", report->id, size-1); | ||
1988 | hid_debug_event(hdev, buff); | ||
1989 | if (raw_data[2] < ARRAY_SIZE(error_codes)) | ||
1990 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n", | ||
1991 | raw_data[2], error_codes[raw_data[2]], raw_data[1]); | ||
1992 | else | ||
1993 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n", | ||
1994 | raw_data[2], raw_data[1]); | ||
1995 | hid_debug_event(hdev, buff); | ||
1996 | break; | ||
1997 | case REPORT_KEY_STATE: | ||
1998 | /* 2 data bytes with key state */ | ||
1999 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2000 | "REPORT_KEY_STATE", report->id, size-1); | ||
2001 | hid_debug_event(hdev, buff); | ||
2002 | if (raw_data[1] == 0) | ||
2003 | snprintf(buff, BUFF_SZ, "\tNo key pressed\n"); | ||
2004 | else if (raw_data[2] == 0) | ||
2005 | snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n", | ||
2006 | raw_data[1], raw_data[1]); | ||
2007 | else | ||
2008 | snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n", | ||
2009 | raw_data[1], raw_data[1], raw_data[2], raw_data[2]); | ||
2010 | hid_debug_event(hdev, buff); | ||
2011 | break; | ||
2012 | case REPORT_IR_DATA: | ||
2013 | /* Up to 20 byes of IR scancode data */ | ||
2014 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2015 | "REPORT_IR_DATA", report->id, size-1); | ||
2016 | hid_debug_event(hdev, buff); | ||
2017 | if (raw_data[1] == 0) { | ||
2018 | snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n"); | ||
2019 | hid_debug_event(hdev, buff); | ||
2020 | } else if (raw_data[1] + 1 <= size) { | ||
2021 | snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ", | ||
2022 | raw_data[1]-1); | ||
2023 | hid_debug_event(hdev, buff); | ||
2024 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1); | ||
2025 | hid_debug_event(hdev, buff); | ||
2026 | } else { | ||
2027 | snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n", | ||
2028 | raw_data[1]-1); | ||
2029 | hid_debug_event(hdev, buff); | ||
2030 | } | ||
2031 | break; | ||
2032 | case REPORT_EE_DATA: | ||
2033 | /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */ | ||
2034 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2035 | "REPORT_EE_DATA", report->id, size-1); | ||
2036 | hid_debug_event(hdev, buff); | ||
2037 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
2038 | raw_data[2], raw_data[1]); | ||
2039 | hid_debug_event(hdev, buff); | ||
2040 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
2041 | hid_debug_event(hdev, buff); | ||
2042 | if (raw_data[3] == 0) { | ||
2043 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
2044 | hid_debug_event(hdev, buff); | ||
2045 | } else if (raw_data[3] + 4 <= size) { | ||
2046 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
2047 | hid_debug_event(hdev, buff); | ||
2048 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
2049 | hid_debug_event(hdev, buff); | ||
2050 | } else { | ||
2051 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
2052 | hid_debug_event(hdev, buff); | ||
2053 | } | ||
2054 | break; | ||
2055 | case REPORT_MEMORY: | ||
2056 | /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */ | ||
2057 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2058 | "REPORT_MEMORY", report->id, size-1); | ||
2059 | hid_debug_event(hdev, buff); | ||
2060 | switch (data->addr_sz) { | ||
2061 | case 2: | ||
2062 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
2063 | raw_data[2], raw_data[1]); | ||
2064 | hid_debug_event(hdev, buff); | ||
2065 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
2066 | hid_debug_event(hdev, buff); | ||
2067 | if (raw_data[3] == 0) { | ||
2068 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
2069 | } else if (raw_data[3] + 4 <= size) { | ||
2070 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
2071 | hid_debug_event(hdev, buff); | ||
2072 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
2073 | } else { | ||
2074 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
2075 | } | ||
2076 | break; | ||
2077 | case 3: | ||
2078 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
2079 | raw_data[3], raw_data[2], raw_data[1]); | ||
2080 | hid_debug_event(hdev, buff); | ||
2081 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
2082 | hid_debug_event(hdev, buff); | ||
2083 | if (raw_data[4] == 0) { | ||
2084 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
2085 | } else if (raw_data[4] + 5 <= size) { | ||
2086 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
2087 | hid_debug_event(hdev, buff); | ||
2088 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
2089 | } else { | ||
2090 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
2091 | } | ||
2092 | break; | ||
2093 | default: | ||
2094 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
2095 | } | ||
2096 | hid_debug_event(hdev, buff); | ||
2097 | break; | ||
2098 | case REPORT_VERSION: | ||
2099 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2100 | "REPORT_VERSION", report->id, size-1); | ||
2101 | hid_debug_event(hdev, buff); | ||
2102 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
2103 | raw_data[2], raw_data[1]); | ||
2104 | hid_debug_event(hdev, buff); | ||
2105 | break; | ||
2106 | case REPORT_BL_ERASE_MEMORY: | ||
2107 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2108 | "REPORT_BL_ERASE_MEMORY", report->id, size-1); | ||
2109 | hid_debug_event(hdev, buff); | ||
2110 | /* TODO */ | ||
2111 | break; | ||
2112 | case REPORT_BL_READ_MEMORY: | ||
2113 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2114 | "REPORT_BL_READ_MEMORY", report->id, size-1); | ||
2115 | hid_debug_event(hdev, buff); | ||
2116 | /* TODO */ | ||
2117 | break; | ||
2118 | case REPORT_BL_WRITE_MEMORY: | ||
2119 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2120 | "REPORT_BL_WRITE_MEMORY", report->id, size-1); | ||
2121 | hid_debug_event(hdev, buff); | ||
2122 | /* TODO */ | ||
2123 | break; | ||
2124 | case REPORT_DEVID: | ||
2125 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2126 | "REPORT_DEVID", report->id, size-1); | ||
2127 | hid_debug_event(hdev, buff); | ||
2128 | snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n", | ||
2129 | raw_data[1], raw_data[2], raw_data[3], raw_data[4]); | ||
2130 | hid_debug_event(hdev, buff); | ||
2131 | snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n", | ||
2132 | raw_data[5]); | ||
2133 | hid_debug_event(hdev, buff); | ||
2134 | break; | ||
2135 | case REPORT_SPLASH_SIZE: | ||
2136 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2137 | "REPORT_SPLASH_SIZE", report->id, size-1); | ||
2138 | hid_debug_event(hdev, buff); | ||
2139 | snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n", | ||
2140 | (raw_data[2] << 8) | raw_data[1]); | ||
2141 | hid_debug_event(hdev, buff); | ||
2142 | snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n", | ||
2143 | (raw_data[4] << 8) | raw_data[3]); | ||
2144 | hid_debug_event(hdev, buff); | ||
2145 | break; | ||
2146 | case REPORT_HOOK_VERSION: | ||
2147 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2148 | "REPORT_HOOK_VERSION", report->id, size-1); | ||
2149 | hid_debug_event(hdev, buff); | ||
2150 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
2151 | raw_data[1], raw_data[2]); | ||
2152 | hid_debug_event(hdev, buff); | ||
2153 | break; | ||
2154 | default: | ||
2155 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
2156 | "<unknown>", report->id, size-1); | ||
2157 | hid_debug_event(hdev, buff); | ||
2158 | break; | ||
2159 | } | ||
2160 | wake_up_interruptible(&hdev->debug_wait); | ||
2161 | kfree(buff); | ||
2162 | } | ||
2163 | |||
2164 | static void picolcd_init_devfs(struct picolcd_data *data, | ||
2165 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
2166 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
2167 | struct hid_report *reset) | ||
2168 | { | ||
2169 | struct hid_device *hdev = data->hdev; | ||
2170 | |||
2171 | mutex_init(&data->mutex_flash); | ||
2172 | |||
2173 | /* reset */ | ||
2174 | if (reset) | ||
2175 | data->debug_reset = debugfs_create_file("reset", 0600, | ||
2176 | hdev->debug_dir, data, &picolcd_debug_reset_fops); | ||
2177 | |||
2178 | /* eeprom */ | ||
2179 | if (eeprom_r || eeprom_w) | ||
2180 | data->debug_eeprom = debugfs_create_file("eeprom", | ||
2181 | (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0), | ||
2182 | hdev->debug_dir, data, &picolcd_debug_eeprom_fops); | ||
2183 | |||
2184 | /* flash */ | ||
2185 | if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8) | ||
2186 | data->addr_sz = flash_r->field[0]->report_count - 1; | ||
2187 | else | ||
2188 | data->addr_sz = -1; | ||
2189 | if (data->addr_sz == 2 || data->addr_sz == 3) { | ||
2190 | data->debug_flash = debugfs_create_file("flash", | ||
2191 | (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0), | ||
2192 | hdev->debug_dir, data, &picolcd_debug_flash_fops); | ||
2193 | } else if (flash_r || flash_w) | ||
2194 | dev_warn(&hdev->dev, "Unexpected FLASH access reports, " | ||
2195 | "please submit rdesc for review\n"); | ||
2196 | } | ||
2197 | |||
2198 | static void picolcd_exit_devfs(struct picolcd_data *data) | ||
2199 | { | ||
2200 | struct dentry *dent; | ||
2201 | |||
2202 | dent = data->debug_reset; | ||
2203 | data->debug_reset = NULL; | ||
2204 | if (dent) | ||
2205 | debugfs_remove(dent); | ||
2206 | dent = data->debug_eeprom; | ||
2207 | data->debug_eeprom = NULL; | ||
2208 | if (dent) | ||
2209 | debugfs_remove(dent); | ||
2210 | dent = data->debug_flash; | ||
2211 | data->debug_flash = NULL; | ||
2212 | if (dent) | ||
2213 | debugfs_remove(dent); | ||
2214 | mutex_destroy(&data->mutex_flash); | ||
2215 | } | ||
2216 | #else | ||
2217 | #define picolcd_debug_raw_event(data, hdev, report, raw_data, size) | ||
2218 | #define picolcd_init_devfs(data, eeprom_r, eeprom_w, flash_r, flash_w, reset) | ||
2219 | static void picolcd_exit_devfs(struct picolcd_data *data) | ||
2220 | { | ||
2221 | } | ||
2222 | #endif /* CONFIG_DEBUG_FS */ | ||
2223 | |||
2224 | /* | ||
2225 | * Handle raw report as sent by device | ||
2226 | */ | ||
2227 | static int picolcd_raw_event(struct hid_device *hdev, | ||
2228 | struct hid_report *report, u8 *raw_data, int size) | ||
2229 | { | ||
2230 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
2231 | unsigned long flags; | ||
2232 | int ret = 0; | ||
2233 | |||
2234 | if (!data) | ||
2235 | return 1; | ||
2236 | |||
2237 | if (report->id == REPORT_KEY_STATE) { | ||
2238 | if (data->input_keys) | ||
2239 | ret = picolcd_raw_keypad(data, report, raw_data+1, size-1); | ||
2240 | } else if (report->id == REPORT_IR_DATA) { | ||
2241 | if (data->input_cir) | ||
2242 | ret = picolcd_raw_cir(data, report, raw_data+1, size-1); | ||
2243 | } else { | ||
2244 | spin_lock_irqsave(&data->lock, flags); | ||
2245 | /* | ||
2246 | * We let the caller of picolcd_send_and_wait() check if the | ||
2247 | * report we got is one of the expected ones or not. | ||
2248 | */ | ||
2249 | if (data->pending) { | ||
2250 | memcpy(data->pending->raw_data, raw_data+1, size-1); | ||
2251 | data->pending->raw_size = size-1; | ||
2252 | data->pending->in_report = report; | ||
2253 | complete(&data->pending->ready); | ||
2254 | } | ||
2255 | spin_unlock_irqrestore(&data->lock, flags); | ||
2256 | } | ||
2257 | |||
2258 | picolcd_debug_raw_event(data, hdev, report, raw_data, size); | ||
2259 | return 1; | ||
2260 | } | ||
2261 | |||
2262 | /* initialize keypad input device */ | ||
2263 | static int picolcd_init_keys(struct picolcd_data *data, | ||
2264 | struct hid_report *report) | ||
2265 | { | ||
2266 | struct hid_device *hdev = data->hdev; | ||
2267 | struct input_dev *idev; | ||
2268 | int error, i; | ||
2269 | |||
2270 | if (!report) | ||
2271 | return -ENODEV; | ||
2272 | if (report->maxfield != 1 || report->field[0]->report_count != 2 || | ||
2273 | report->field[0]->report_size != 8) { | ||
2274 | dev_err(&hdev->dev, "unsupported KEY_STATE report"); | ||
2275 | return -EINVAL; | ||
2276 | } | ||
2277 | |||
2278 | idev = input_allocate_device(); | ||
2279 | if (idev == NULL) { | ||
2280 | dev_err(&hdev->dev, "failed to allocate input device"); | ||
2281 | return -ENOMEM; | ||
2282 | } | ||
2283 | input_set_drvdata(idev, hdev); | ||
2284 | memcpy(data->keycode, def_keymap, sizeof(def_keymap)); | ||
2285 | idev->name = hdev->name; | ||
2286 | idev->phys = hdev->phys; | ||
2287 | idev->uniq = hdev->uniq; | ||
2288 | idev->id.bustype = hdev->bus; | ||
2289 | idev->id.vendor = hdev->vendor; | ||
2290 | idev->id.product = hdev->product; | ||
2291 | idev->id.version = hdev->version; | ||
2292 | idev->dev.parent = hdev->dev.parent; | ||
2293 | idev->keycode = &data->keycode; | ||
2294 | idev->keycodemax = PICOLCD_KEYS; | ||
2295 | idev->keycodesize = sizeof(data->keycode[0]); | ||
2296 | input_set_capability(idev, EV_MSC, MSC_SCAN); | ||
2297 | set_bit(EV_REP, idev->evbit); | ||
2298 | for (i = 0; i < PICOLCD_KEYS; i++) | ||
2299 | input_set_capability(idev, EV_KEY, data->keycode[i]); | ||
2300 | error = input_register_device(idev); | ||
2301 | if (error) { | ||
2302 | dev_err(&hdev->dev, "error registering the input device"); | ||
2303 | input_free_device(idev); | ||
2304 | return error; | ||
2305 | } | ||
2306 | data->input_keys = idev; | ||
2307 | return 0; | ||
2308 | } | ||
2309 | |||
2310 | static void picolcd_exit_keys(struct picolcd_data *data) | ||
2311 | { | ||
2312 | struct input_dev *idev = data->input_keys; | ||
2313 | |||
2314 | data->input_keys = NULL; | ||
2315 | if (idev) | ||
2316 | input_unregister_device(idev); | ||
2317 | } | ||
2318 | |||
2319 | /* initialize CIR input device */ | ||
2320 | static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) | ||
2321 | { | ||
2322 | /* support not implemented yet */ | ||
2323 | return 0; | ||
2324 | } | ||
2325 | |||
2326 | static inline void picolcd_exit_cir(struct picolcd_data *data) | ||
2327 | { | ||
2328 | } | ||
2329 | |||
2330 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | ||
2331 | { | ||
2332 | int error; | ||
2333 | |||
2334 | error = picolcd_check_version(hdev); | ||
2335 | if (error) | ||
2336 | return error; | ||
2337 | |||
2338 | if (data->version[0] != 0 && data->version[1] != 3) | ||
2339 | dev_info(&hdev->dev, "Device with untested firmware revision, " | ||
2340 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
2341 | dev_name(&hdev->dev)); | ||
2342 | |||
2343 | /* Setup keypad input device */ | ||
2344 | error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev)); | ||
2345 | if (error) | ||
2346 | goto err; | ||
2347 | |||
2348 | /* Setup CIR input device */ | ||
2349 | error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev)); | ||
2350 | if (error) | ||
2351 | goto err; | ||
2352 | |||
2353 | /* Set up the framebuffer device */ | ||
2354 | error = picolcd_init_framebuffer(data); | ||
2355 | if (error) | ||
2356 | goto err; | ||
2357 | |||
2358 | /* Setup lcd class device */ | ||
2359 | error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev)); | ||
2360 | if (error) | ||
2361 | goto err; | ||
2362 | |||
2363 | /* Setup backlight class device */ | ||
2364 | error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev)); | ||
2365 | if (error) | ||
2366 | goto err; | ||
2367 | |||
2368 | /* Setup the LED class devices */ | ||
2369 | error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev)); | ||
2370 | if (error) | ||
2371 | goto err; | ||
2372 | |||
2373 | picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev), | ||
2374 | picolcd_out_report(REPORT_EE_WRITE, hdev), | ||
2375 | picolcd_out_report(REPORT_READ_MEMORY, hdev), | ||
2376 | picolcd_out_report(REPORT_WRITE_MEMORY, hdev), | ||
2377 | picolcd_out_report(REPORT_RESET, hdev)); | ||
2378 | return 0; | ||
2379 | err: | ||
2380 | picolcd_exit_leds(data); | ||
2381 | picolcd_exit_backlight(data); | ||
2382 | picolcd_exit_lcd(data); | ||
2383 | picolcd_exit_framebuffer(data); | ||
2384 | picolcd_exit_cir(data); | ||
2385 | picolcd_exit_keys(data); | ||
2386 | return error; | ||
2387 | } | ||
2388 | |||
2389 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) | ||
2390 | { | ||
2391 | int error; | ||
2392 | |||
2393 | error = picolcd_check_version(hdev); | ||
2394 | if (error) | ||
2395 | return error; | ||
2396 | |||
2397 | if (data->version[0] != 1 && data->version[1] != 0) | ||
2398 | dev_info(&hdev->dev, "Device with untested bootloader revision, " | ||
2399 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
2400 | dev_name(&hdev->dev)); | ||
2401 | |||
2402 | picolcd_init_devfs(data, NULL, NULL, | ||
2403 | picolcd_out_report(REPORT_BL_READ_MEMORY, hdev), | ||
2404 | picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL); | ||
2405 | return 0; | ||
2406 | } | ||
2407 | |||
2408 | static int picolcd_probe(struct hid_device *hdev, | ||
2409 | const struct hid_device_id *id) | ||
2410 | { | ||
2411 | struct picolcd_data *data; | ||
2412 | int error = -ENOMEM; | ||
2413 | |||
2414 | dbg_hid(PICOLCD_NAME " hardware probe...\n"); | ||
2415 | |||
2416 | /* | ||
2417 | * Let's allocate the picolcd data structure, set some reasonable | ||
2418 | * defaults, and associate it with the device | ||
2419 | */ | ||
2420 | data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL); | ||
2421 | if (data == NULL) { | ||
2422 | dev_err(&hdev->dev, "can't allocate space for Minibox PicoLCD device data\n"); | ||
2423 | error = -ENOMEM; | ||
2424 | goto err_no_cleanup; | ||
2425 | } | ||
2426 | |||
2427 | spin_lock_init(&data->lock); | ||
2428 | mutex_init(&data->mutex); | ||
2429 | data->hdev = hdev; | ||
2430 | data->opmode_delay = 5000; | ||
2431 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
2432 | data->status |= PICOLCD_BOOTLOADER; | ||
2433 | hid_set_drvdata(hdev, data); | ||
2434 | |||
2435 | /* Parse the device reports and start it up */ | ||
2436 | error = hid_parse(hdev); | ||
2437 | if (error) { | ||
2438 | dev_err(&hdev->dev, "device report parse failed\n"); | ||
2439 | goto err_cleanup_data; | ||
2440 | } | ||
2441 | |||
2442 | /* We don't use hidinput but hid_hw_start() fails if nothing is | ||
2443 | * claimed. So spoof claimed input. */ | ||
2444 | hdev->claimed = HID_CLAIMED_INPUT; | ||
2445 | error = hid_hw_start(hdev, 0); | ||
2446 | hdev->claimed = 0; | ||
2447 | if (error) { | ||
2448 | dev_err(&hdev->dev, "hardware start failed\n"); | ||
2449 | goto err_cleanup_data; | ||
2450 | } | ||
2451 | |||
2452 | error = hdev->ll_driver->open(hdev); | ||
2453 | if (error) { | ||
2454 | dev_err(&hdev->dev, "failed to open input interrupt pipe for key and IR events\n"); | ||
2455 | goto err_cleanup_hid_hw; | ||
2456 | } | ||
2457 | |||
2458 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
2459 | if (error) { | ||
2460 | dev_err(&hdev->dev, "failed to create sysfs attributes\n"); | ||
2461 | goto err_cleanup_hid_ll; | ||
2462 | } | ||
2463 | |||
2464 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode); | ||
2465 | if (error) { | ||
2466 | dev_err(&hdev->dev, "failed to create sysfs attributes\n"); | ||
2467 | goto err_cleanup_sysfs1; | ||
2468 | } | ||
2469 | |||
2470 | if (data->status & PICOLCD_BOOTLOADER) | ||
2471 | error = picolcd_probe_bootloader(hdev, data); | ||
2472 | else | ||
2473 | error = picolcd_probe_lcd(hdev, data); | ||
2474 | if (error) | ||
2475 | goto err_cleanup_sysfs2; | ||
2476 | |||
2477 | dbg_hid(PICOLCD_NAME " activated and initialized\n"); | ||
2478 | return 0; | ||
2479 | |||
2480 | err_cleanup_sysfs2: | ||
2481 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
2482 | err_cleanup_sysfs1: | ||
2483 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
2484 | err_cleanup_hid_ll: | ||
2485 | hdev->ll_driver->close(hdev); | ||
2486 | err_cleanup_hid_hw: | ||
2487 | hid_hw_stop(hdev); | ||
2488 | err_cleanup_data: | ||
2489 | kfree(data); | ||
2490 | err_no_cleanup: | ||
2491 | hid_set_drvdata(hdev, NULL); | ||
2492 | |||
2493 | return error; | ||
2494 | } | ||
2495 | |||
2496 | static void picolcd_remove(struct hid_device *hdev) | ||
2497 | { | ||
2498 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
2499 | unsigned long flags; | ||
2500 | |||
2501 | dbg_hid(PICOLCD_NAME " hardware remove...\n"); | ||
2502 | spin_lock_irqsave(&data->lock, flags); | ||
2503 | data->status |= PICOLCD_FAILED; | ||
2504 | spin_unlock_irqrestore(&data->lock, flags); | ||
2505 | |||
2506 | picolcd_exit_devfs(data); | ||
2507 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
2508 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
2509 | hdev->ll_driver->close(hdev); | ||
2510 | hid_hw_stop(hdev); | ||
2511 | hid_set_drvdata(hdev, NULL); | ||
2512 | |||
2513 | /* Shortcut potential pending reply that will never arrive */ | ||
2514 | spin_lock_irqsave(&data->lock, flags); | ||
2515 | if (data->pending) | ||
2516 | complete(&data->pending->ready); | ||
2517 | spin_unlock_irqrestore(&data->lock, flags); | ||
2518 | |||
2519 | /* Cleanup LED */ | ||
2520 | picolcd_exit_leds(data); | ||
2521 | /* Clean up the framebuffer */ | ||
2522 | picolcd_exit_backlight(data); | ||
2523 | picolcd_exit_lcd(data); | ||
2524 | picolcd_exit_framebuffer(data); | ||
2525 | /* Cleanup input */ | ||
2526 | picolcd_exit_cir(data); | ||
2527 | picolcd_exit_keys(data); | ||
2528 | |||
2529 | mutex_destroy(&data->mutex); | ||
2530 | /* Finally, clean up the picolcd data itself */ | ||
2531 | kfree(data); | ||
2532 | } | ||
2533 | |||
2534 | static const struct hid_device_id picolcd_devices[] = { | ||
2535 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
2536 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
2537 | { } | ||
2538 | }; | ||
2539 | MODULE_DEVICE_TABLE(hid, picolcd_devices); | ||
2540 | |||
2541 | static struct hid_driver picolcd_driver = { | ||
2542 | .name = "hid-picolcd", | ||
2543 | .id_table = picolcd_devices, | ||
2544 | .probe = picolcd_probe, | ||
2545 | .remove = picolcd_remove, | ||
2546 | .raw_event = picolcd_raw_event, | ||
2547 | }; | ||
2548 | |||
2549 | static int __init picolcd_init(void) | ||
2550 | { | ||
2551 | return hid_register_driver(&picolcd_driver); | ||
2552 | } | ||
2553 | |||
2554 | static void __exit picolcd_exit(void) | ||
2555 | { | ||
2556 | hid_unregister_driver(&picolcd_driver); | ||
2557 | } | ||
2558 | |||
2559 | module_init(picolcd_init); | ||
2560 | module_exit(picolcd_exit); | ||
2561 | MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver"); | ||
2562 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 14a67fba590e..17eb3ec3fbb8 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
@@ -623,6 +623,7 @@ int usbhid_wait_io(struct hid_device *hid) | |||
623 | 623 | ||
624 | return 0; | 624 | return 0; |
625 | } | 625 | } |
626 | EXPORT_SYMBOL_GPL(usbhid_wait_io); | ||
626 | 627 | ||
627 | static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) | 628 | static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) |
628 | { | 629 | { |