Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/hid-chicony.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* HID driver for some chicony "special" devices
4
*
5
* Copyright (c) 1999 Andreas Gal
6
* Copyright (c) 2000-2005 Vojtech Pavlik <[email protected]>
7
* Copyright (c) 2005 Michael Haboustak <[email protected]> for Concept2, Inc
8
* Copyright (c) 2006-2007 Jiri Kosina
9
* Copyright (c) 2007 Paul Walmsley
10
* Copyright (c) 2008 Jiri Slaby
11
*/
12
13
/*
14
*/
15
16
#include <linux/device.h>
17
#include <linux/input.h>
18
#include <linux/hid.h>
19
#include <linux/module.h>
20
#include <linux/usb.h>
21
22
#include "hid-ids.h"
23
24
#define CH_WIRELESS_CTL_REPORT_ID 0x11
25
26
static int ch_report_wireless(struct hid_report *report, u8 *data, int size)
27
{
28
struct hid_device *hdev = report->device;
29
struct input_dev *input;
30
31
if (report->id != CH_WIRELESS_CTL_REPORT_ID || report->maxfield != 1)
32
return 0;
33
34
input = report->field[0]->hidinput->input;
35
if (!input) {
36
hid_warn(hdev, "can't find wireless radio control's input");
37
return 0;
38
}
39
40
input_report_key(input, KEY_RFKILL, 1);
41
input_sync(input);
42
input_report_key(input, KEY_RFKILL, 0);
43
input_sync(input);
44
45
return 1;
46
}
47
48
static int ch_raw_event(struct hid_device *hdev,
49
struct hid_report *report, u8 *data, int size)
50
{
51
if (report->application == HID_GD_WIRELESS_RADIO_CTLS)
52
return ch_report_wireless(report, data, size);
53
54
return 0;
55
}
56
57
#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
58
EV_KEY, (c))
59
static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
60
struct hid_field *field, struct hid_usage *usage,
61
unsigned long **bit, int *max)
62
{
63
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
64
return 0;
65
66
set_bit(EV_REP, hi->input->evbit);
67
switch (usage->hid & HID_USAGE) {
68
case 0xff01: ch_map_key_clear(BTN_1); break;
69
case 0xff02: ch_map_key_clear(BTN_2); break;
70
case 0xff03: ch_map_key_clear(BTN_3); break;
71
case 0xff04: ch_map_key_clear(BTN_4); break;
72
case 0xff05: ch_map_key_clear(BTN_5); break;
73
case 0xff06: ch_map_key_clear(BTN_6); break;
74
case 0xff07: ch_map_key_clear(BTN_7); break;
75
case 0xff08: ch_map_key_clear(BTN_8); break;
76
case 0xff09: ch_map_key_clear(BTN_9); break;
77
case 0xff0a: ch_map_key_clear(BTN_A); break;
78
case 0xff0b: ch_map_key_clear(BTN_B); break;
79
case 0x00f1: ch_map_key_clear(KEY_WLAN); break;
80
case 0x00f2: ch_map_key_clear(KEY_BRIGHTNESSDOWN); break;
81
case 0x00f3: ch_map_key_clear(KEY_BRIGHTNESSUP); break;
82
case 0x00f4: ch_map_key_clear(KEY_DISPLAY_OFF); break;
83
case 0x00f7: ch_map_key_clear(KEY_CAMERA); break;
84
case 0x00f8: ch_map_key_clear(KEY_PROG1); break;
85
default:
86
return 0;
87
}
88
return 1;
89
}
90
91
static const __u8 *ch_switch12_report_fixup(struct hid_device *hdev,
92
__u8 *rdesc, unsigned int *rsize)
93
{
94
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
95
96
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
97
/* Change usage maximum and logical maximum from 0x7fff to
98
* 0x2fff, so they don't exceed HID_MAX_USAGES */
99
switch (hdev->product) {
100
case USB_DEVICE_ID_CHICONY_ACER_SWITCH12:
101
if (*rsize >= 128 && rdesc[64] == 0xff && rdesc[65] == 0x7f
102
&& rdesc[69] == 0xff && rdesc[70] == 0x7f) {
103
hid_info(hdev, "Fixing up report descriptor\n");
104
rdesc[65] = rdesc[70] = 0x2f;
105
}
106
break;
107
}
108
109
}
110
return rdesc;
111
}
112
113
static int ch_probe(struct hid_device *hdev, const struct hid_device_id *id)
114
{
115
int ret;
116
117
if (!hid_is_usb(hdev))
118
return -EINVAL;
119
120
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
121
ret = hid_parse(hdev);
122
if (ret) {
123
hid_err(hdev, "Chicony hid parse failed: %d\n", ret);
124
return ret;
125
}
126
127
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
128
if (ret) {
129
hid_err(hdev, "Chicony hw start failed: %d\n", ret);
130
return ret;
131
}
132
133
return 0;
134
}
135
136
static const struct hid_device_id ch_devices[] = {
137
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
138
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
139
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS3) },
140
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) },
141
{ }
142
};
143
MODULE_DEVICE_TABLE(hid, ch_devices);
144
145
static struct hid_driver ch_driver = {
146
.name = "chicony",
147
.id_table = ch_devices,
148
.report_fixup = ch_switch12_report_fixup,
149
.input_mapping = ch_input_mapping,
150
.probe = ch_probe,
151
.raw_event = ch_raw_event,
152
};
153
module_hid_driver(ch_driver);
154
155
MODULE_DESCRIPTION("HID driver for some chicony \"special\" devices");
156
MODULE_LICENSE("GPL");
157
158