mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
fe73965d07
By allowing the drivers to return a "const *" they can constify their static report arrays. This makes it clear to driver authors that the HID core will not modify those reports and they can be reused for multiple devices. Furthermore security is slightly improved as those reports are protected against accidental or malicious modifications. [bentiss: fixup hid-cougar.c and hid-multitouch.c for latest version of the master branch] Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> Link: https://patch.msgid.link/20240803-hid-const-fixup-v2-6-f53d7a7b29d8@weissschuh.net Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
210 lines
4.0 KiB
C
210 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* HID driver for zydacron remote control
|
|
*
|
|
* Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
|
|
*/
|
|
|
|
/*
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/hid.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "hid-ids.h"
|
|
|
|
struct zc_device {
|
|
struct input_dev *input_ep81;
|
|
unsigned short last_key[4];
|
|
};
|
|
|
|
|
|
/*
|
|
* Zydacron remote control has an invalid HID report descriptor,
|
|
* that needs fixing before we can parse it.
|
|
*/
|
|
static const __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|
unsigned int *rsize)
|
|
{
|
|
if (*rsize >= 253 &&
|
|
rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff &&
|
|
rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff &&
|
|
rdesc[0xe1] == 0xbc && rdesc[0xe2] == 0xff) {
|
|
hid_info(hdev,
|
|
"fixing up zydacron remote control report descriptor\n");
|
|
rdesc[0x96] = rdesc[0xca] = rdesc[0xe1] = 0x0c;
|
|
rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00;
|
|
}
|
|
return rdesc;
|
|
}
|
|
|
|
#define zc_map_key_clear(c) \
|
|
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
|
|
|
static int zc_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
unsigned long **bit, int *max)
|
|
{
|
|
int i;
|
|
struct zc_device *zc = hid_get_drvdata(hdev);
|
|
zc->input_ep81 = hi->input;
|
|
|
|
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
|
|
return 0;
|
|
|
|
dbg_hid("zynacron input mapping event [0x%x]\n",
|
|
usage->hid & HID_USAGE);
|
|
|
|
switch (usage->hid & HID_USAGE) {
|
|
/* report 2 */
|
|
case 0x10:
|
|
zc_map_key_clear(KEY_MODE);
|
|
break;
|
|
case 0x30:
|
|
zc_map_key_clear(KEY_SCREEN);
|
|
break;
|
|
case 0x70:
|
|
zc_map_key_clear(KEY_INFO);
|
|
break;
|
|
/* report 3 */
|
|
case 0x04:
|
|
zc_map_key_clear(KEY_RADIO);
|
|
break;
|
|
/* report 4 */
|
|
case 0x0d:
|
|
zc_map_key_clear(KEY_PVR);
|
|
break;
|
|
case 0x25:
|
|
zc_map_key_clear(KEY_TV);
|
|
break;
|
|
case 0x47:
|
|
zc_map_key_clear(KEY_AUDIO);
|
|
break;
|
|
case 0x49:
|
|
zc_map_key_clear(KEY_AUX);
|
|
break;
|
|
case 0x4a:
|
|
zc_map_key_clear(KEY_VIDEO);
|
|
break;
|
|
case 0x48:
|
|
zc_map_key_clear(KEY_DVD);
|
|
break;
|
|
case 0x24:
|
|
zc_map_key_clear(KEY_MENU);
|
|
break;
|
|
case 0x32:
|
|
zc_map_key_clear(KEY_TEXT);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
zc->last_key[i] = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int zc_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|
u8 *data, int size)
|
|
{
|
|
struct zc_device *zc = hid_get_drvdata(hdev);
|
|
int ret = 0;
|
|
unsigned key;
|
|
unsigned short index;
|
|
|
|
if (report->id == data[0]) {
|
|
|
|
/* break keys */
|
|
for (index = 0; index < 4; index++) {
|
|
key = zc->last_key[index];
|
|
if (key) {
|
|
input_event(zc->input_ep81, EV_KEY, key, 0);
|
|
zc->last_key[index] = 0;
|
|
}
|
|
}
|
|
|
|
key = 0;
|
|
switch (report->id) {
|
|
case 0x02:
|
|
case 0x03:
|
|
switch (data[1]) {
|
|
case 0x10:
|
|
key = KEY_MODE;
|
|
index = 0;
|
|
break;
|
|
case 0x30:
|
|
key = KEY_SCREEN;
|
|
index = 1;
|
|
break;
|
|
case 0x70:
|
|
key = KEY_INFO;
|
|
index = 2;
|
|
break;
|
|
case 0x04:
|
|
key = KEY_RADIO;
|
|
index = 3;
|
|
break;
|
|
}
|
|
|
|
if (key) {
|
|
input_event(zc->input_ep81, EV_KEY, key, 1);
|
|
zc->last_key[index] = key;
|
|
}
|
|
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
{
|
|
int ret;
|
|
struct zc_device *zc;
|
|
|
|
zc = devm_kzalloc(&hdev->dev, sizeof(*zc), GFP_KERNEL);
|
|
if (zc == NULL) {
|
|
hid_err(hdev, "can't alloc descriptor\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
hid_set_drvdata(hdev, zc);
|
|
|
|
ret = hid_parse(hdev);
|
|
if (ret) {
|
|
hid_err(hdev, "parse failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
|
if (ret) {
|
|
hid_err(hdev, "hw start failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct hid_device_id zc_devices[] = {
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(hid, zc_devices);
|
|
|
|
static struct hid_driver zc_driver = {
|
|
.name = "zydacron",
|
|
.id_table = zc_devices,
|
|
.report_fixup = zc_report_fixup,
|
|
.input_mapping = zc_input_mapping,
|
|
.raw_event = zc_raw_event,
|
|
.probe = zc_probe,
|
|
};
|
|
module_hid_driver(zc_driver);
|
|
|
|
MODULE_DESCRIPTION("HID driver for zydacron remote control");
|
|
MODULE_LICENSE("GPL");
|