// SPDX-License-Identifier: GPL-2.0-only /*************************************************************************** * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * * * * Based on Logitech G13 driver (v0.4) * * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * * *
***************************************************************************/
/* Input device * * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys * and header for 4x4 key matrix. The built-in keys are part of the matrix.
*/ staticconstunsignedshort def_keymap[PICOLCD_KEYS] = {
KEY_RESERVED, /* none */
KEY_BACK, /* col 4 + row 1 */
KEY_HOMEPAGE, /* col 3 + row 1 */
KEY_RESERVED, /* col 2 + row 1 */
KEY_RESERVED, /* col 1 + row 1 */
KEY_SCROLLUP, /* col 4 + row 2 */
KEY_OK, /* col 3 + row 2 */
KEY_SCROLLDOWN, /* col 2 + row 2 */
KEY_RESERVED, /* col 1 + row 2 */
KEY_RESERVED, /* col 4 + row 3 */
KEY_RESERVED, /* col 3 + row 3 */
KEY_RESERVED, /* col 2 + row 3 */
KEY_RESERVED, /* col 1 + row 3 */
KEY_RESERVED, /* col 4 + row 4 */
KEY_RESERVED, /* col 3 + row 4 */
KEY_RESERVED, /* col 2 + row 4 */
KEY_RESERVED, /* col 1 + row 4 */
};
/* Find a given report */ struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
{ struct list_head *feature_report_list = &hdev->report_enum[dir].report_list; struct hid_report *report = NULL;
list_for_each_entry(report, feature_report_list, list) { if (report->id == id) return report;
}
hid_warn(hdev, "No report with id 0x%x found\n", id); return NULL;
}
/* Submit a report and wait for a reply from device - if device fades away
* or does not respond in time, return NULL */ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, int report_id, const u8 *raw_data, int size)
{ struct picolcd_data *data = hid_get_drvdata(hdev); struct picolcd_pending *work; struct hid_report *report = picolcd_out_report(report_id, hdev); unsignedlong flags; int i, j, k;
if (!report || !data) return NULL; if (data->status & PICOLCD_FAILED) return NULL;
work = kzalloc(sizeof(*work), GFP_KERNEL); if (!work) return NULL;
mutex_lock(&data->mutex);
spin_lock_irqsave(&data->lock, flags); for (i = k = 0; i < report->maxfield; i++) for (j = 0; j < report->field[i]->report_count; j++) {
hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
k++;
} if (data->status & PICOLCD_FAILED) {
kfree(work);
work = NULL;
} else {
data->pending = work;
hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
spin_unlock_irqrestore(&data->lock, flags);
wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
spin_lock_irqsave(&data->lock, flags);
data->pending = NULL;
}
spin_unlock_irqrestore(&data->lock, flags);
mutex_unlock(&data->mutex); return work;
}
/* * input class device
*/ staticint picolcd_raw_keypad(struct picolcd_data *data, struct hid_report *report, u8 *raw_data, int size)
{ /* * Keypad event * First and second data bytes list currently pressed keys, * 0x00 means no key and at most 2 keys may be pressed at same time
*/ int i, j;
/* determine newly pressed keys */ for (i = 0; i < size; i++) { unsignedint key_code; if (raw_data[i] == 0) continue; for (j = 0; j < sizeof(data->pressed_keys); j++) if (data->pressed_keys[j] == raw_data[i]) goto key_already_down; for (j = 0; j < sizeof(data->pressed_keys); j++) if (data->pressed_keys[j] == 0) {
data->pressed_keys[j] = raw_data[i]; break;
}
input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]); if (raw_data[i] < PICOLCD_KEYS)
key_code = data->keycode[raw_data[i]]; else
key_code = KEY_UNKNOWN; if (key_code != KEY_UNKNOWN) {
dbg_hid(PICOLCD_NAME " got key press for %u:%d",
raw_data[i], key_code);
input_report_key(data->input_keys, key_code, 1);
}
input_sync(data->input_keys);
key_already_down: continue;
}
/* determine newly released keys */ for (j = 0; j < sizeof(data->pressed_keys); j++) { unsignedint key_code; if (data->pressed_keys[j] == 0) continue; for (i = 0; i < size; i++) if (data->pressed_keys[j] == raw_data[i]) goto key_still_down;
input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]); if (data->pressed_keys[j] < PICOLCD_KEYS)
key_code = data->keycode[data->pressed_keys[j]]; else
key_code = KEY_UNKNOWN; if (key_code != KEY_UNKNOWN) {
dbg_hid(PICOLCD_NAME " got key release for %u:%d",
data->pressed_keys[j], key_code);
input_report_key(data->input_keys, key_code, 0);
}
input_sync(data->input_keys);
data->pressed_keys[j] = 0;
key_still_down: continue;
} return 1;
}
staticint picolcd_check_version(struct hid_device *hdev)
{ struct picolcd_data *data = hid_get_drvdata(hdev); struct picolcd_pending *verinfo; int ret = 0;
if (!data) return -ENODEV;
verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0); if (!verinfo) {
hid_err(hdev, "no version response from PicoLCD\n"); return -ENODEV;
}
if (verinfo->raw_size == 2) {
data->version[0] = verinfo->raw_data[1];
data->version[1] = verinfo->raw_data[0]; if (data->status & PICOLCD_BOOTLOADER) {
hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
verinfo->raw_data[1], verinfo->raw_data[0]);
} else {
hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
verinfo->raw_data[1], verinfo->raw_data[0]);
}
} else {
hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
ret = -EINVAL;
}
kfree(verinfo); return ret;
}
/* * Reset our device and wait for answer to VERSION request
*/ int picolcd_reset(struct hid_device *hdev)
{ struct picolcd_data *data = hid_get_drvdata(hdev); struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); unsignedlong flags; int error;
if (!data || !report || report->maxfield != 1) return -ENODEV;
spin_lock_irqsave(&data->lock, flags); if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
data->status |= PICOLCD_BOOTLOADER;
/* * Handle raw report as sent by device
*/ staticint picolcd_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *raw_data, int size)
{ struct picolcd_data *data = hid_get_drvdata(hdev); unsignedlong flags;
if (!data) return 1;
if (size > 64) {
hid_warn(hdev, "invalid size value (%d) for picolcd raw event (%d)\n",
size, report->id); return 0;
}
if (report->id == REPORT_KEY_STATE) { if (data->input_keys)
picolcd_raw_keypad(data, report, raw_data+1, size-1);
} elseif (report->id == REPORT_IR_DATA) {
picolcd_raw_cir(data, report, raw_data+1, size-1);
} else {
spin_lock_irqsave(&data->lock, flags); /* * We let the caller of picolcd_send_and_wait() check if the * report we got is one of the expected ones or not.
*/ if (data->pending) {
memcpy(data->pending->raw_data, raw_data+1, size-1);
data->pending->raw_size = size-1;
data->pending->in_report = report;
complete(&data->pending->ready);
}
spin_unlock_irqrestore(&data->lock, flags);
}
/* * Let's allocate the picolcd data structure, set some reasonable * defaults, and associate it with the device
*/
data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL); if (data == NULL) {
hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n"); return -ENOMEM;
}
/* Parse the device reports and start it up */
error = hid_parse(hdev); if (error) {
hid_err(hdev, "device report parse failed\n"); goto err_cleanup_data;
}
/* Shortcut potential pending reply that will never arrive */
spin_lock_irqsave(&data->lock, flags); if (data->pending)
complete(&data->pending->ready);
spin_unlock_irqrestore(&data->lock, flags);
/* Cleanup LED */
picolcd_exit_leds(data); /* Clean up the framebuffer */
picolcd_exit_framebuffer(data);
picolcd_exit_backlight(data);
picolcd_exit_lcd(data); /* Cleanup input */
picolcd_exit_cir(data);
picolcd_exit_keys(data);
mutex_destroy(&data->mutex); /* Finally, clean up the picolcd data itself */
kfree(data);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.