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