661 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/***************************************************************************3 * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *4 * *5 * Based on Logitech G13 driver (v0.4) *6 * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *7 * *8 ***************************************************************************/9 10#include <linux/hid.h>11#include <linux/hid-debug.h>12#include <linux/input.h>13#include "hid-ids.h"14 15#include <linux/fb.h>16#include <linux/vmalloc.h>17 18#include <linux/completion.h>19#include <linux/uaccess.h>20#include <linux/module.h>21#include <linux/string.h>22 23#include "hid-picolcd.h"24 25 26/* Input device27 *28 * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys29 * and header for 4x4 key matrix. The built-in keys are part of the matrix.30 */31static const unsigned short def_keymap[PICOLCD_KEYS] = {32 KEY_RESERVED, /* none */33 KEY_BACK, /* col 4 + row 1 */34 KEY_HOMEPAGE, /* col 3 + row 1 */35 KEY_RESERVED, /* col 2 + row 1 */36 KEY_RESERVED, /* col 1 + row 1 */37 KEY_SCROLLUP, /* col 4 + row 2 */38 KEY_OK, /* col 3 + row 2 */39 KEY_SCROLLDOWN, /* col 2 + row 2 */40 KEY_RESERVED, /* col 1 + row 2 */41 KEY_RESERVED, /* col 4 + row 3 */42 KEY_RESERVED, /* col 3 + row 3 */43 KEY_RESERVED, /* col 2 + row 3 */44 KEY_RESERVED, /* col 1 + row 3 */45 KEY_RESERVED, /* col 4 + row 4 */46 KEY_RESERVED, /* col 3 + row 4 */47 KEY_RESERVED, /* col 2 + row 4 */48 KEY_RESERVED, /* col 1 + row 4 */49};50 51 52/* Find a given report */53struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)54{55 struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;56 struct hid_report *report = NULL;57 58 list_for_each_entry(report, feature_report_list, list) {59 if (report->id == id)60 return report;61 }62 hid_warn(hdev, "No report with id 0x%x found\n", id);63 return NULL;64}65 66/* Submit a report and wait for a reply from device - if device fades away67 * or does not respond in time, return NULL */68struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,69 int report_id, const u8 *raw_data, int size)70{71 struct picolcd_data *data = hid_get_drvdata(hdev);72 struct picolcd_pending *work;73 struct hid_report *report = picolcd_out_report(report_id, hdev);74 unsigned long flags;75 int i, j, k;76 77 if (!report || !data)78 return NULL;79 if (data->status & PICOLCD_FAILED)80 return NULL;81 work = kzalloc(sizeof(*work), GFP_KERNEL);82 if (!work)83 return NULL;84 85 init_completion(&work->ready);86 work->out_report = report;87 work->in_report = NULL;88 work->raw_size = 0;89 90 mutex_lock(&data->mutex);91 spin_lock_irqsave(&data->lock, flags);92 for (i = k = 0; i < report->maxfield; i++)93 for (j = 0; j < report->field[i]->report_count; j++) {94 hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);95 k++;96 }97 if (data->status & PICOLCD_FAILED) {98 kfree(work);99 work = NULL;100 } else {101 data->pending = work;102 hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);103 spin_unlock_irqrestore(&data->lock, flags);104 wait_for_completion_interruptible_timeout(&work->ready, HZ*2);105 spin_lock_irqsave(&data->lock, flags);106 data->pending = NULL;107 }108 spin_unlock_irqrestore(&data->lock, flags);109 mutex_unlock(&data->mutex);110 return work;111}112 113/*114 * input class device115 */116static int picolcd_raw_keypad(struct picolcd_data *data,117 struct hid_report *report, u8 *raw_data, int size)118{119 /*120 * Keypad event121 * First and second data bytes list currently pressed keys,122 * 0x00 means no key and at most 2 keys may be pressed at same time123 */124 int i, j;125 126 /* determine newly pressed keys */127 for (i = 0; i < size; i++) {128 unsigned int key_code;129 if (raw_data[i] == 0)130 continue;131 for (j = 0; j < sizeof(data->pressed_keys); j++)132 if (data->pressed_keys[j] == raw_data[i])133 goto key_already_down;134 for (j = 0; j < sizeof(data->pressed_keys); j++)135 if (data->pressed_keys[j] == 0) {136 data->pressed_keys[j] = raw_data[i];137 break;138 }139 input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);140 if (raw_data[i] < PICOLCD_KEYS)141 key_code = data->keycode[raw_data[i]];142 else143 key_code = KEY_UNKNOWN;144 if (key_code != KEY_UNKNOWN) {145 dbg_hid(PICOLCD_NAME " got key press for %u:%d",146 raw_data[i], key_code);147 input_report_key(data->input_keys, key_code, 1);148 }149 input_sync(data->input_keys);150key_already_down:151 continue;152 }153 154 /* determine newly released keys */155 for (j = 0; j < sizeof(data->pressed_keys); j++) {156 unsigned int key_code;157 if (data->pressed_keys[j] == 0)158 continue;159 for (i = 0; i < size; i++)160 if (data->pressed_keys[j] == raw_data[i])161 goto key_still_down;162 input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);163 if (data->pressed_keys[j] < PICOLCD_KEYS)164 key_code = data->keycode[data->pressed_keys[j]];165 else166 key_code = KEY_UNKNOWN;167 if (key_code != KEY_UNKNOWN) {168 dbg_hid(PICOLCD_NAME " got key release for %u:%d",169 data->pressed_keys[j], key_code);170 input_report_key(data->input_keys, key_code, 0);171 }172 input_sync(data->input_keys);173 data->pressed_keys[j] = 0;174key_still_down:175 continue;176 }177 return 1;178}179 180static int picolcd_check_version(struct hid_device *hdev)181{182 struct picolcd_data *data = hid_get_drvdata(hdev);183 struct picolcd_pending *verinfo;184 int ret = 0;185 186 if (!data)187 return -ENODEV;188 189 verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);190 if (!verinfo) {191 hid_err(hdev, "no version response from PicoLCD\n");192 return -ENODEV;193 }194 195 if (verinfo->raw_size == 2) {196 data->version[0] = verinfo->raw_data[1];197 data->version[1] = verinfo->raw_data[0];198 if (data->status & PICOLCD_BOOTLOADER) {199 hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",200 verinfo->raw_data[1], verinfo->raw_data[0]);201 } else {202 hid_info(hdev, "PicoLCD, firmware version %d.%d\n",203 verinfo->raw_data[1], verinfo->raw_data[0]);204 }205 } else {206 hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");207 ret = -EINVAL;208 }209 kfree(verinfo);210 return ret;211}212 213/*214 * Reset our device and wait for answer to VERSION request215 */216int picolcd_reset(struct hid_device *hdev)217{218 struct picolcd_data *data = hid_get_drvdata(hdev);219 struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);220 unsigned long flags;221 int error;222 223 if (!data || !report || report->maxfield != 1)224 return -ENODEV;225 226 spin_lock_irqsave(&data->lock, flags);227 if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)228 data->status |= PICOLCD_BOOTLOADER;229 230 /* perform the reset */231 hid_set_field(report->field[0], 0, 1);232 if (data->status & PICOLCD_FAILED) {233 spin_unlock_irqrestore(&data->lock, flags);234 return -ENODEV;235 }236 hid_hw_request(hdev, report, HID_REQ_SET_REPORT);237 spin_unlock_irqrestore(&data->lock, flags);238 239 error = picolcd_check_version(hdev);240 if (error)241 return error;242 243 picolcd_resume_lcd(data);244 picolcd_resume_backlight(data);245 picolcd_fb_refresh(data);246 picolcd_leds_set(data);247 return 0;248}249 250/*251 * The "operation_mode" sysfs attribute252 */253static ssize_t picolcd_operation_mode_show(struct device *dev,254 struct device_attribute *attr, char *buf)255{256 struct picolcd_data *data = dev_get_drvdata(dev);257 258 if (data->status & PICOLCD_BOOTLOADER)259 return sysfs_emit(buf, "[bootloader] lcd\n");260 else261 return sysfs_emit(buf, "bootloader [lcd]\n");262}263 264static ssize_t picolcd_operation_mode_store(struct device *dev,265 struct device_attribute *attr, const char *buf, size_t count)266{267 struct picolcd_data *data = dev_get_drvdata(dev);268 struct hid_report *report = NULL;269 int timeout = data->opmode_delay;270 unsigned long flags;271 272 if (sysfs_streq(buf, "lcd")) {273 if (data->status & PICOLCD_BOOTLOADER)274 report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);275 } else if (sysfs_streq(buf, "bootloader")) {276 if (!(data->status & PICOLCD_BOOTLOADER))277 report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);278 } else {279 return -EINVAL;280 }281 282 if (!report || report->maxfield != 1)283 return -EINVAL;284 285 spin_lock_irqsave(&data->lock, flags);286 hid_set_field(report->field[0], 0, timeout & 0xff);287 hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);288 hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);289 spin_unlock_irqrestore(&data->lock, flags);290 return count;291}292 293static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,294 picolcd_operation_mode_store);295 296/*297 * The "operation_mode_delay" sysfs attribute298 */299static ssize_t picolcd_operation_mode_delay_show(struct device *dev,300 struct device_attribute *attr, char *buf)301{302 struct picolcd_data *data = dev_get_drvdata(dev);303 304 return sysfs_emit(buf, "%hu\n", data->opmode_delay);305}306 307static ssize_t picolcd_operation_mode_delay_store(struct device *dev,308 struct device_attribute *attr, const char *buf, size_t count)309{310 struct picolcd_data *data = dev_get_drvdata(dev);311 unsigned u;312 if (sscanf(buf, "%u", &u) != 1)313 return -EINVAL;314 if (u > 30000)315 return -EINVAL;316 else317 data->opmode_delay = u;318 return count;319}320 321static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,322 picolcd_operation_mode_delay_store);323 324/*325 * Handle raw report as sent by device326 */327static int picolcd_raw_event(struct hid_device *hdev,328 struct hid_report *report, u8 *raw_data, int size)329{330 struct picolcd_data *data = hid_get_drvdata(hdev);331 unsigned long flags;332 333 if (!data)334 return 1;335 336 if (size > 64) {337 hid_warn(hdev, "invalid size value (%d) for picolcd raw event (%d)\n",338 size, report->id);339 return 0;340 }341 342 if (report->id == REPORT_KEY_STATE) {343 if (data->input_keys)344 picolcd_raw_keypad(data, report, raw_data+1, size-1);345 } else if (report->id == REPORT_IR_DATA) {346 picolcd_raw_cir(data, report, raw_data+1, size-1);347 } else {348 spin_lock_irqsave(&data->lock, flags);349 /*350 * We let the caller of picolcd_send_and_wait() check if the351 * report we got is one of the expected ones or not.352 */353 if (data->pending) {354 memcpy(data->pending->raw_data, raw_data+1, size-1);355 data->pending->raw_size = size-1;356 data->pending->in_report = report;357 complete(&data->pending->ready);358 }359 spin_unlock_irqrestore(&data->lock, flags);360 }361 362 picolcd_debug_raw_event(data, hdev, report, raw_data, size);363 return 1;364}365 366#ifdef CONFIG_PM367static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)368{369 if (PMSG_IS_AUTO(message))370 return 0;371 372 picolcd_suspend_backlight(hid_get_drvdata(hdev));373 dbg_hid(PICOLCD_NAME " device ready for suspend\n");374 return 0;375}376 377static int picolcd_resume(struct hid_device *hdev)378{379 int ret;380 ret = picolcd_resume_backlight(hid_get_drvdata(hdev));381 if (ret)382 dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);383 return 0;384}385 386static int picolcd_reset_resume(struct hid_device *hdev)387{388 int ret;389 ret = picolcd_reset(hdev);390 if (ret)391 dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);392 ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);393 if (ret)394 dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);395 ret = picolcd_resume_lcd(hid_get_drvdata(hdev));396 if (ret)397 dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);398 ret = picolcd_resume_backlight(hid_get_drvdata(hdev));399 if (ret)400 dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);401 picolcd_leds_set(hid_get_drvdata(hdev));402 return 0;403}404#endif405 406/* initialize keypad input device */407static int picolcd_init_keys(struct picolcd_data *data,408 struct hid_report *report)409{410 struct hid_device *hdev = data->hdev;411 struct input_dev *idev;412 int error, i;413 414 if (!report)415 return -ENODEV;416 if (report->maxfield != 1 || report->field[0]->report_count != 2 ||417 report->field[0]->report_size != 8) {418 hid_err(hdev, "unsupported KEY_STATE report\n");419 return -EINVAL;420 }421 422 idev = input_allocate_device();423 if (idev == NULL) {424 hid_err(hdev, "failed to allocate input device\n");425 return -ENOMEM;426 }427 input_set_drvdata(idev, hdev);428 memcpy(data->keycode, def_keymap, sizeof(def_keymap));429 idev->name = hdev->name;430 idev->phys = hdev->phys;431 idev->uniq = hdev->uniq;432 idev->id.bustype = hdev->bus;433 idev->id.vendor = hdev->vendor;434 idev->id.product = hdev->product;435 idev->id.version = hdev->version;436 idev->dev.parent = &hdev->dev;437 idev->keycode = &data->keycode;438 idev->keycodemax = PICOLCD_KEYS;439 idev->keycodesize = sizeof(data->keycode[0]);440 input_set_capability(idev, EV_MSC, MSC_SCAN);441 set_bit(EV_REP, idev->evbit);442 for (i = 0; i < PICOLCD_KEYS; i++)443 input_set_capability(idev, EV_KEY, data->keycode[i]);444 error = input_register_device(idev);445 if (error) {446 hid_err(hdev, "error registering the input device\n");447 input_free_device(idev);448 return error;449 }450 data->input_keys = idev;451 return 0;452}453 454static void picolcd_exit_keys(struct picolcd_data *data)455{456 struct input_dev *idev = data->input_keys;457 458 data->input_keys = NULL;459 if (idev)460 input_unregister_device(idev);461}462 463static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)464{465 int error;466 467 /* Setup keypad input device */468 error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));469 if (error)470 goto err;471 472 /* Setup CIR input device */473 error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));474 if (error)475 goto err;476 477 /* Setup lcd class device */478 error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));479 if (error)480 goto err;481 482 /* Setup backlight class device */483 error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));484 if (error)485 goto err;486 487 /* Set up the framebuffer device */488 error = picolcd_init_framebuffer(data);489 if (error)490 goto err;491 492 /* Setup the LED class devices */493 error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));494 if (error)495 goto err;496 497 picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),498 picolcd_out_report(REPORT_EE_WRITE, hdev),499 picolcd_out_report(REPORT_READ_MEMORY, hdev),500 picolcd_out_report(REPORT_WRITE_MEMORY, hdev),501 picolcd_out_report(REPORT_RESET, hdev));502 return 0;503err:504 picolcd_exit_leds(data);505 picolcd_exit_framebuffer(data);506 picolcd_exit_backlight(data);507 picolcd_exit_lcd(data);508 picolcd_exit_cir(data);509 picolcd_exit_keys(data);510 return error;511}512 513static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)514{515 picolcd_init_devfs(data, NULL, NULL,516 picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),517 picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);518 return 0;519}520 521static int picolcd_probe(struct hid_device *hdev,522 const struct hid_device_id *id)523{524 struct picolcd_data *data;525 int error = -ENOMEM;526 527 dbg_hid(PICOLCD_NAME " hardware probe...\n");528 529 /*530 * Let's allocate the picolcd data structure, set some reasonable531 * defaults, and associate it with the device532 */533 data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);534 if (data == NULL) {535 hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");536 return -ENOMEM;537 }538 539 spin_lock_init(&data->lock);540 mutex_init(&data->mutex);541 data->hdev = hdev;542 data->opmode_delay = 5000;543 if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)544 data->status |= PICOLCD_BOOTLOADER;545 hid_set_drvdata(hdev, data);546 547 /* Parse the device reports and start it up */548 error = hid_parse(hdev);549 if (error) {550 hid_err(hdev, "device report parse failed\n");551 goto err_cleanup_data;552 }553 554 error = hid_hw_start(hdev, 0);555 if (error) {556 hid_err(hdev, "hardware start failed\n");557 goto err_cleanup_data;558 }559 560 error = hid_hw_open(hdev);561 if (error) {562 hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");563 goto err_cleanup_hid_hw;564 }565 566 error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);567 if (error) {568 hid_err(hdev, "failed to create sysfs attributes\n");569 goto err_cleanup_hid_ll;570 }571 572 error = device_create_file(&hdev->dev, &dev_attr_operation_mode);573 if (error) {574 hid_err(hdev, "failed to create sysfs attributes\n");575 goto err_cleanup_sysfs1;576 }577 578 if (data->status & PICOLCD_BOOTLOADER)579 error = picolcd_probe_bootloader(hdev, data);580 else581 error = picolcd_probe_lcd(hdev, data);582 if (error)583 goto err_cleanup_sysfs2;584 585 dbg_hid(PICOLCD_NAME " activated and initialized\n");586 return 0;587 588err_cleanup_sysfs2:589 device_remove_file(&hdev->dev, &dev_attr_operation_mode);590err_cleanup_sysfs1:591 device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);592err_cleanup_hid_ll:593 hid_hw_close(hdev);594err_cleanup_hid_hw:595 hid_hw_stop(hdev);596err_cleanup_data:597 kfree(data);598 return error;599}600 601static void picolcd_remove(struct hid_device *hdev)602{603 struct picolcd_data *data = hid_get_drvdata(hdev);604 unsigned long flags;605 606 dbg_hid(PICOLCD_NAME " hardware remove...\n");607 spin_lock_irqsave(&data->lock, flags);608 data->status |= PICOLCD_FAILED;609 spin_unlock_irqrestore(&data->lock, flags);610 611 picolcd_exit_devfs(data);612 device_remove_file(&hdev->dev, &dev_attr_operation_mode);613 device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);614 hid_hw_close(hdev);615 hid_hw_stop(hdev);616 617 /* Shortcut potential pending reply that will never arrive */618 spin_lock_irqsave(&data->lock, flags);619 if (data->pending)620 complete(&data->pending->ready);621 spin_unlock_irqrestore(&data->lock, flags);622 623 /* Cleanup LED */624 picolcd_exit_leds(data);625 /* Clean up the framebuffer */626 picolcd_exit_framebuffer(data);627 picolcd_exit_backlight(data);628 picolcd_exit_lcd(data);629 /* Cleanup input */630 picolcd_exit_cir(data);631 picolcd_exit_keys(data);632 633 mutex_destroy(&data->mutex);634 /* Finally, clean up the picolcd data itself */635 kfree(data);636}637 638static const struct hid_device_id picolcd_devices[] = {639 { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },640 { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },641 { }642};643MODULE_DEVICE_TABLE(hid, picolcd_devices);644 645static struct hid_driver picolcd_driver = {646 .name = "hid-picolcd",647 .id_table = picolcd_devices,648 .probe = picolcd_probe,649 .remove = picolcd_remove,650 .raw_event = picolcd_raw_event,651#ifdef CONFIG_PM652 .suspend = picolcd_suspend,653 .resume = picolcd_resume,654 .reset_resume = picolcd_reset_resume,655#endif656};657module_hid_driver(picolcd_driver);658 659MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");660MODULE_LICENSE("GPL v2");661