cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hid-picolcd_core.c (18384B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/***************************************************************************
      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 device
     27 *
     28 * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
     29 * 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 away
     67 * 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 device
    115 */
    116static int picolcd_raw_keypad(struct picolcd_data *data,
    117		struct hid_report *report, u8 *raw_data, int size)
    118{
    119	/*
    120	 * Keypad event
    121	 * First and second data bytes list currently pressed keys,
    122	 * 0x00 means no key and at most 2 keys may be pressed at same time
    123	 */
    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		else
    143			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		else
    166			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 request
    215 */
    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 attribute
    252 */
    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 snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
    260	else
    261		return snprintf(buf, PAGE_SIZE, "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 attribute
    298 */
    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 snprintf(buf, PAGE_SIZE, "%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	else
    317		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 device
    326 */
    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 the
    351		 * 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_PM
    367static 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#endif
    405
    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	/* Set up the framebuffer device */
    478	error = picolcd_init_framebuffer(data);
    479	if (error)
    480		goto err;
    481
    482	/* Setup lcd class device */
    483	error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
    484	if (error)
    485		goto err;
    486
    487	/* Setup backlight class device */
    488	error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
    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_backlight(data);
    506	picolcd_exit_lcd(data);
    507	picolcd_exit_framebuffer(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 reasonable
    531	 * defaults, and associate it with the device
    532	 */
    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	else
    581		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_backlight(data);
    627	picolcd_exit_lcd(data);
    628	picolcd_exit_framebuffer(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_PM
    652	.suspend =       picolcd_suspend,
    653	.resume =        picolcd_resume,
    654	.reset_resume =  picolcd_reset_resume,
    655#endif
    656};
    657module_hid_driver(picolcd_driver);
    658
    659MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
    660MODULE_LICENSE("GPL v2");