File Index
Files and directories
Sign in
FILE

hid-winwing.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/hid-winwing.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3/*
  4 * HID driver for WinWing Orion 2 throttle
  5 *
  6 * Copyright (c) 2023 Ivan Gorinov
  7 */
  8
  9#include <linux/device.h>
 10#include <linux/hid.h>
 11#include <linux/hidraw.h>
 12#include <linux/kernel.h>
 13#include <linux/module.h>
 14#include <linux/mutex.h>
 15
 16#define MAX_REPORT 16
 17
 18struct winwing_led {
 19	struct led_classdev cdev;
 20	struct hid_device *hdev;
 21	int number;
 22};
 23
 24struct winwing_led_info {
 25	int number;
 26	int max_brightness;
 27	const char *led_name;
 28};
 29
 30static const struct winwing_led_info led_info[3] = {
 31	{ 0, 255, "backlight" },
 32	{ 1, 1, "a-a" },
 33	{ 2, 1, "a-g" },
 34};
 35
 36struct winwing_drv_data {
 37	struct hid_device *hdev;
 38	__u8 *report_buf;
 39	struct mutex lock;
 40	int map_more_buttons;
 41	unsigned int num_leds;
 42	struct winwing_led leds[];
 43};
 44
 45static int winwing_led_write(struct led_classdev *cdev,
 46		enum led_brightness br)
 47{
 48	struct winwing_led *led = (struct winwing_led *) cdev;
 49	struct winwing_drv_data *data = hid_get_drvdata(led->hdev);
 50	__u8 *buf = data->report_buf;
 51	int ret;
 52
 53	mutex_lock(&data->lock);
 54
 55	buf[0] = 0x02;
 56	buf[1] = 0x60;
 57	buf[2] = 0xbe;
 58	buf[3] = 0x00;
 59	buf[4] = 0x00;
 60	buf[5] = 0x03;
 61	buf[6] = 0x49;
 62	buf[7] = led->number;
 63	buf[8] = br;
 64	buf[9] = 0x00;
 65	buf[10] = 0;
 66	buf[11] = 0;
 67	buf[12] = 0;
 68	buf[13] = 0;
 69
 70	ret = hid_hw_output_report(led->hdev, buf, 14);
 71
 72	mutex_unlock(&data->lock);
 73
 74	return ret;
 75}
 76
 77static int winwing_init_led(struct hid_device *hdev,
 78		struct input_dev *input)
 79{
 80	struct winwing_drv_data *data;
 81	struct winwing_led *led;
 82	int ret;
 83	int i;
 84
 85	data = hid_get_drvdata(hdev);
 86
 87	if (!data)
 88		return -EINVAL;
 89
 90	data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
 91
 92	if (!data->report_buf)
 93		return -ENOMEM;
 94
 95	for (i = 0; i < 3; i += 1) {
 96		const struct winwing_led_info *info = &led_info[i];
 97
 98		led = &data->leds[i];
 99		led->hdev = hdev;
100		led->number = info->number;
101		led->cdev.max_brightness = info->max_brightness;
102		led->cdev.brightness_set_blocking = winwing_led_write;
103		led->cdev.flags = LED_HW_PLUGGABLE;
104		led->cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
105						"%s::%s",
106						dev_name(&input->dev),
107						info->led_name);
108
109		if (!led->cdev.name)
110			return -ENOMEM;
111
112		ret = devm_led_classdev_register(&hdev->dev, &led->cdev);
113		if (ret)
114			return ret;
115	}
116
117	return ret;
118}
119
120static int winwing_map_button(int button, int map_more_buttons)
121{
122	if (button < 1)
123		return KEY_RESERVED;
124
125	if (button > 112)
126		return KEY_RESERVED;
127
128	if (button <= 16) {
129		/*
130		 * Grip buttons [1 .. 16] are mapped to
131		 * key codes BTN_TRIGGER .. BTN_DEAD
132		 */
133		return (button - 1) + BTN_JOYSTICK;
134	}
135
136	if (button >= 65) {
137		/*
138		 * Base buttons [65 .. 112] are mapped to
139		 * key codes BTN_TRIGGER_HAPPY17 .. KEY_MAX
140		 */
141		return (button - 65) + BTN_TRIGGER_HAPPY17;
142	}
143
144	if (!map_more_buttons) {
145		/*
146		 * Not mapping numbers [33 .. 64] which
147		 * are not assigned to any real buttons
148		 */
149		if (button >= 33)
150			return KEY_RESERVED;
151		/*
152		 * Grip buttons [17 .. 32] are mapped to
153		 * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16
154		 */
155		return (button - 17) + BTN_TRIGGER_HAPPY1;
156	}
157
158	if (button >= 49) {
159		/*
160		 * Grip buttons [49 .. 64] are mapped to
161		 * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16
162		 */
163		return (button - 49) + BTN_TRIGGER_HAPPY1;
164	}
165
166	/*
167	 * Grip buttons [17 .. 44] are mapped to
168	 * key codes KEY_MACRO1 .. KEY_MACRO28;
169	 * also mapping numbers [45 .. 48] which
170	 * are not assigned to any real buttons.
171	 */
172	return (button - 17) + KEY_MACRO1;
173}
174
175static int winwing_input_mapping(struct hid_device *hdev,
176	struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
177	unsigned long **bit, int *max)
178{
179	struct winwing_drv_data *data;
180	int code = KEY_RESERVED;
181	int button = 0;
182
183	data = hid_get_drvdata(hdev);
184
185	if (!data)
186		return -EINVAL;
187
188	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
189		return 0;
190
191	if (field->application != HID_GD_JOYSTICK)
192		return 0;
193
194	/* Button numbers start with 1 */
195	button = usage->hid & HID_USAGE;
196
197	code = winwing_map_button(button, data->map_more_buttons);
198
199	hid_map_usage(hi, usage, bit, max, EV_KEY, code);
200
201	return 1;
202}
203
204static int winwing_probe(struct hid_device *hdev,
205		const struct hid_device_id *id)
206{
207	struct winwing_drv_data *data;
208	size_t data_size = struct_size(data, leds, 3);
209	int ret;
210
211	ret = hid_parse(hdev);
212	if (ret) {
213		hid_err(hdev, "parse failed\n");
214		return ret;
215	}
216
217	data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL);
218
219	if (!data)
220		return -ENOMEM;
221
222	data->map_more_buttons = id->driver_data;
223
224	hid_set_drvdata(hdev, data);
225
226	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
227	if (ret) {
228		hid_err(hdev, "hw start failed\n");
229		return ret;
230	}
231
232	return 0;
233}
234
235static int winwing_input_configured(struct hid_device *hdev,
236		struct hid_input *hidinput)
237{
238	int ret;
239
240	ret = winwing_init_led(hdev, hidinput->input);
241
242	if (ret)
243		hid_err(hdev, "led init failed\n");
244
245	return ret;
246}
247
248static const struct hid_device_id winwing_devices[] = {
249	{ HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 },  /* TGRIP-15E  */
250	{ HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 },  /* TGRIP-15EX */
251	{ HID_USB_DEVICE(0x4098, 0xbe68), .driver_data = 0 },  /* TGRIP-16EX */
252	{ HID_USB_DEVICE(0x4098, 0xbe62), .driver_data = 0 },  /* TGRIP-18   */
253	{}
254};
255
256MODULE_DEVICE_TABLE(hid, winwing_devices);
257
258static struct hid_driver winwing_driver = {
259	.name = "winwing",
260	.id_table = winwing_devices,
261	.input_configured = winwing_input_configured,
262	.input_mapping = winwing_input_mapping,
263	.probe = winwing_probe,
264};
265module_hid_driver(winwing_driver);
266
267MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle");
268MODULE_LICENSE("GPL");
~/ Read-only file preview.