xref: /openbmc/linux/drivers/hid/hid-google-stadiaff.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1*24175157SFabio Baltieri // SPDX-License-Identifier: GPL-2.0-or-later
2*24175157SFabio Baltieri /*
3*24175157SFabio Baltieri  * Stadia controller rumble support.
4*24175157SFabio Baltieri  *
5*24175157SFabio Baltieri  * Copyright 2023 Google LLC
6*24175157SFabio Baltieri  */
7*24175157SFabio Baltieri 
8*24175157SFabio Baltieri #include <linux/hid.h>
9*24175157SFabio Baltieri #include <linux/input.h>
10*24175157SFabio Baltieri #include <linux/slab.h>
11*24175157SFabio Baltieri #include <linux/module.h>
12*24175157SFabio Baltieri 
13*24175157SFabio Baltieri #include "hid-ids.h"
14*24175157SFabio Baltieri 
15*24175157SFabio Baltieri #define STADIA_FF_REPORT_ID 5
16*24175157SFabio Baltieri 
17*24175157SFabio Baltieri struct stadiaff_device {
18*24175157SFabio Baltieri 	struct hid_device *hid;
19*24175157SFabio Baltieri 	struct hid_report *report;
20*24175157SFabio Baltieri 	spinlock_t lock;
21*24175157SFabio Baltieri 	bool removed;
22*24175157SFabio Baltieri 	uint16_t strong_magnitude;
23*24175157SFabio Baltieri 	uint16_t weak_magnitude;
24*24175157SFabio Baltieri 	struct work_struct work;
25*24175157SFabio Baltieri };
26*24175157SFabio Baltieri 
stadiaff_work(struct work_struct * work)27*24175157SFabio Baltieri static void stadiaff_work(struct work_struct *work)
28*24175157SFabio Baltieri {
29*24175157SFabio Baltieri 	struct stadiaff_device *stadiaff =
30*24175157SFabio Baltieri 		container_of(work, struct stadiaff_device, work);
31*24175157SFabio Baltieri 	struct hid_field *rumble_field = stadiaff->report->field[0];
32*24175157SFabio Baltieri 	unsigned long flags;
33*24175157SFabio Baltieri 
34*24175157SFabio Baltieri 	spin_lock_irqsave(&stadiaff->lock, flags);
35*24175157SFabio Baltieri 	rumble_field->value[0] = stadiaff->strong_magnitude;
36*24175157SFabio Baltieri 	rumble_field->value[1] = stadiaff->weak_magnitude;
37*24175157SFabio Baltieri 	spin_unlock_irqrestore(&stadiaff->lock, flags);
38*24175157SFabio Baltieri 
39*24175157SFabio Baltieri 	hid_hw_request(stadiaff->hid, stadiaff->report, HID_REQ_SET_REPORT);
40*24175157SFabio Baltieri }
41*24175157SFabio Baltieri 
stadiaff_play(struct input_dev * dev,void * data,struct ff_effect * effect)42*24175157SFabio Baltieri static int stadiaff_play(struct input_dev *dev, void *data,
43*24175157SFabio Baltieri 			 struct ff_effect *effect)
44*24175157SFabio Baltieri {
45*24175157SFabio Baltieri 	struct hid_device *hid = input_get_drvdata(dev);
46*24175157SFabio Baltieri 	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
47*24175157SFabio Baltieri 	unsigned long flags;
48*24175157SFabio Baltieri 
49*24175157SFabio Baltieri 	spin_lock_irqsave(&stadiaff->lock, flags);
50*24175157SFabio Baltieri 	if (!stadiaff->removed) {
51*24175157SFabio Baltieri 		stadiaff->strong_magnitude = effect->u.rumble.strong_magnitude;
52*24175157SFabio Baltieri 		stadiaff->weak_magnitude = effect->u.rumble.weak_magnitude;
53*24175157SFabio Baltieri 		schedule_work(&stadiaff->work);
54*24175157SFabio Baltieri 	}
55*24175157SFabio Baltieri 	spin_unlock_irqrestore(&stadiaff->lock, flags);
56*24175157SFabio Baltieri 
57*24175157SFabio Baltieri 	return 0;
58*24175157SFabio Baltieri }
59*24175157SFabio Baltieri 
stadiaff_init(struct hid_device * hid)60*24175157SFabio Baltieri static int stadiaff_init(struct hid_device *hid)
61*24175157SFabio Baltieri {
62*24175157SFabio Baltieri 	struct stadiaff_device *stadiaff;
63*24175157SFabio Baltieri 	struct hid_report *report;
64*24175157SFabio Baltieri 	struct hid_input *hidinput;
65*24175157SFabio Baltieri 	struct input_dev *dev;
66*24175157SFabio Baltieri 	int error;
67*24175157SFabio Baltieri 
68*24175157SFabio Baltieri 	if (list_empty(&hid->inputs)) {
69*24175157SFabio Baltieri 		hid_err(hid, "no inputs found\n");
70*24175157SFabio Baltieri 		return -ENODEV;
71*24175157SFabio Baltieri 	}
72*24175157SFabio Baltieri 	hidinput = list_entry(hid->inputs.next, struct hid_input, list);
73*24175157SFabio Baltieri 	dev = hidinput->input;
74*24175157SFabio Baltieri 
75*24175157SFabio Baltieri 	report = hid_validate_values(hid, HID_OUTPUT_REPORT,
76*24175157SFabio Baltieri 				     STADIA_FF_REPORT_ID, 0, 2);
77*24175157SFabio Baltieri 	if (!report)
78*24175157SFabio Baltieri 		return -ENODEV;
79*24175157SFabio Baltieri 
80*24175157SFabio Baltieri 	stadiaff = devm_kzalloc(&hid->dev, sizeof(struct stadiaff_device),
81*24175157SFabio Baltieri 				GFP_KERNEL);
82*24175157SFabio Baltieri 	if (!stadiaff)
83*24175157SFabio Baltieri 		return -ENOMEM;
84*24175157SFabio Baltieri 
85*24175157SFabio Baltieri 	hid_set_drvdata(hid, stadiaff);
86*24175157SFabio Baltieri 
87*24175157SFabio Baltieri 	input_set_capability(dev, EV_FF, FF_RUMBLE);
88*24175157SFabio Baltieri 
89*24175157SFabio Baltieri 	error = input_ff_create_memless(dev, NULL, stadiaff_play);
90*24175157SFabio Baltieri 	if (error)
91*24175157SFabio Baltieri 		return error;
92*24175157SFabio Baltieri 
93*24175157SFabio Baltieri 	stadiaff->removed = false;
94*24175157SFabio Baltieri 	stadiaff->hid = hid;
95*24175157SFabio Baltieri 	stadiaff->report = report;
96*24175157SFabio Baltieri 	INIT_WORK(&stadiaff->work, stadiaff_work);
97*24175157SFabio Baltieri 	spin_lock_init(&stadiaff->lock);
98*24175157SFabio Baltieri 
99*24175157SFabio Baltieri 	hid_info(hid, "Force Feedback for Google Stadia controller\n");
100*24175157SFabio Baltieri 
101*24175157SFabio Baltieri 	return 0;
102*24175157SFabio Baltieri }
103*24175157SFabio Baltieri 
stadia_probe(struct hid_device * hdev,const struct hid_device_id * id)104*24175157SFabio Baltieri static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id)
105*24175157SFabio Baltieri {
106*24175157SFabio Baltieri 	int ret;
107*24175157SFabio Baltieri 
108*24175157SFabio Baltieri 	ret = hid_parse(hdev);
109*24175157SFabio Baltieri 	if (ret) {
110*24175157SFabio Baltieri 		hid_err(hdev, "parse failed\n");
111*24175157SFabio Baltieri 		return ret;
112*24175157SFabio Baltieri 	}
113*24175157SFabio Baltieri 
114*24175157SFabio Baltieri 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
115*24175157SFabio Baltieri 	if (ret) {
116*24175157SFabio Baltieri 		hid_err(hdev, "hw start failed\n");
117*24175157SFabio Baltieri 		return ret;
118*24175157SFabio Baltieri 	}
119*24175157SFabio Baltieri 
120*24175157SFabio Baltieri 	ret = stadiaff_init(hdev);
121*24175157SFabio Baltieri 	if (ret) {
122*24175157SFabio Baltieri 		hid_err(hdev, "force feedback init failed\n");
123*24175157SFabio Baltieri 		hid_hw_stop(hdev);
124*24175157SFabio Baltieri 		return ret;
125*24175157SFabio Baltieri 	}
126*24175157SFabio Baltieri 
127*24175157SFabio Baltieri 	return 0;
128*24175157SFabio Baltieri }
129*24175157SFabio Baltieri 
stadia_remove(struct hid_device * hid)130*24175157SFabio Baltieri static void stadia_remove(struct hid_device *hid)
131*24175157SFabio Baltieri {
132*24175157SFabio Baltieri 	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
133*24175157SFabio Baltieri 	unsigned long flags;
134*24175157SFabio Baltieri 
135*24175157SFabio Baltieri 	spin_lock_irqsave(&stadiaff->lock, flags);
136*24175157SFabio Baltieri 	stadiaff->removed = true;
137*24175157SFabio Baltieri 	spin_unlock_irqrestore(&stadiaff->lock, flags);
138*24175157SFabio Baltieri 
139*24175157SFabio Baltieri 	cancel_work_sync(&stadiaff->work);
140*24175157SFabio Baltieri 	hid_hw_stop(hid);
141*24175157SFabio Baltieri }
142*24175157SFabio Baltieri 
143*24175157SFabio Baltieri static const struct hid_device_id stadia_devices[] = {
144*24175157SFabio Baltieri 	{ HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
145*24175157SFabio Baltieri 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
146*24175157SFabio Baltieri 	{ }
147*24175157SFabio Baltieri };
148*24175157SFabio Baltieri MODULE_DEVICE_TABLE(hid, stadia_devices);
149*24175157SFabio Baltieri 
150*24175157SFabio Baltieri static struct hid_driver stadia_driver = {
151*24175157SFabio Baltieri 	.name = "stadia",
152*24175157SFabio Baltieri 	.id_table = stadia_devices,
153*24175157SFabio Baltieri 	.probe = stadia_probe,
154*24175157SFabio Baltieri 	.remove = stadia_remove,
155*24175157SFabio Baltieri };
156*24175157SFabio Baltieri module_hid_driver(stadia_driver);
157*24175157SFabio Baltieri 
158*24175157SFabio Baltieri MODULE_LICENSE("GPL");
159