hc
2024-05-14 bedbef8ad3e75a304af6361af235302bcc61d06b
kernel/drivers/hid/hid-bigbenff.c
....@@ -174,6 +174,7 @@
174174 struct bigben_device {
175175 struct hid_device *hid;
176176 struct hid_report *report;
177
+ spinlock_t lock;
177178 bool removed;
178179 u8 led_state; /* LED1 = 1 .. LED4 = 8 */
179180 u8 right_motor_on; /* right motor off/on 0/1 */
....@@ -184,18 +185,39 @@
184185 struct work_struct worker;
185186 };
186187
188
+static inline void bigben_schedule_work(struct bigben_device *bigben)
189
+{
190
+ unsigned long flags;
191
+
192
+ spin_lock_irqsave(&bigben->lock, flags);
193
+ if (!bigben->removed)
194
+ schedule_work(&bigben->worker);
195
+ spin_unlock_irqrestore(&bigben->lock, flags);
196
+}
187197
188198 static void bigben_worker(struct work_struct *work)
189199 {
190200 struct bigben_device *bigben = container_of(work,
191201 struct bigben_device, worker);
192202 struct hid_field *report_field = bigben->report->field[0];
203
+ bool do_work_led = false;
204
+ bool do_work_ff = false;
205
+ u8 *buf;
206
+ u32 len;
207
+ unsigned long flags;
193208
194
- if (bigben->removed || !report_field)
209
+ buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
210
+ if (!buf)
195211 return;
212
+
213
+ len = hid_report_len(bigben->report);
214
+
215
+ /* LED work */
216
+ spin_lock_irqsave(&bigben->lock, flags);
196217
197218 if (bigben->work_led) {
198219 bigben->work_led = false;
220
+ do_work_led = true;
199221 report_field->value[0] = 0x01; /* 1 = led message */
200222 report_field->value[1] = 0x08; /* reserved value, always 8 */
201223 report_field->value[2] = bigben->led_state;
....@@ -204,11 +226,22 @@
204226 report_field->value[5] = 0x00; /* padding */
205227 report_field->value[6] = 0x00; /* padding */
206228 report_field->value[7] = 0x00; /* padding */
207
- hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
229
+ hid_output_report(bigben->report, buf);
208230 }
231
+
232
+ spin_unlock_irqrestore(&bigben->lock, flags);
233
+
234
+ if (do_work_led) {
235
+ hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
236
+ bigben->report->type, HID_REQ_SET_REPORT);
237
+ }
238
+
239
+ /* FF work */
240
+ spin_lock_irqsave(&bigben->lock, flags);
209241
210242 if (bigben->work_ff) {
211243 bigben->work_ff = false;
244
+ do_work_ff = true;
212245 report_field->value[0] = 0x02; /* 2 = rumble effect message */
213246 report_field->value[1] = 0x08; /* reserved value, always 8 */
214247 report_field->value[2] = bigben->right_motor_on;
....@@ -217,8 +250,17 @@
217250 report_field->value[5] = 0x00; /* padding */
218251 report_field->value[6] = 0x00; /* padding */
219252 report_field->value[7] = 0x00; /* padding */
220
- hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
253
+ hid_output_report(bigben->report, buf);
221254 }
255
+
256
+ spin_unlock_irqrestore(&bigben->lock, flags);
257
+
258
+ if (do_work_ff) {
259
+ hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
260
+ bigben->report->type, HID_REQ_SET_REPORT);
261
+ }
262
+
263
+ kfree(buf);
222264 }
223265
224266 static int hid_bigben_play_effect(struct input_dev *dev, void *data,
....@@ -228,6 +270,7 @@
228270 struct bigben_device *bigben = hid_get_drvdata(hid);
229271 u8 right_motor_on;
230272 u8 left_motor_force;
273
+ unsigned long flags;
231274
232275 if (!bigben) {
233276 hid_err(hid, "no device data\n");
....@@ -242,10 +285,13 @@
242285
243286 if (right_motor_on != bigben->right_motor_on ||
244287 left_motor_force != bigben->left_motor_force) {
288
+ spin_lock_irqsave(&bigben->lock, flags);
245289 bigben->right_motor_on = right_motor_on;
246290 bigben->left_motor_force = left_motor_force;
247291 bigben->work_ff = true;
248
- schedule_work(&bigben->worker);
292
+ spin_unlock_irqrestore(&bigben->lock, flags);
293
+
294
+ bigben_schedule_work(bigben);
249295 }
250296
251297 return 0;
....@@ -259,6 +305,7 @@
259305 struct bigben_device *bigben = hid_get_drvdata(hid);
260306 int n;
261307 bool work;
308
+ unsigned long flags;
262309
263310 if (!bigben) {
264311 hid_err(hid, "no device data\n");
....@@ -267,6 +314,7 @@
267314
268315 for (n = 0; n < NUM_LEDS; n++) {
269316 if (led == bigben->leds[n]) {
317
+ spin_lock_irqsave(&bigben->lock, flags);
270318 if (value == LED_OFF) {
271319 work = (bigben->led_state & BIT(n));
272320 bigben->led_state &= ~BIT(n);
....@@ -274,10 +322,11 @@
274322 work = !(bigben->led_state & BIT(n));
275323 bigben->led_state |= BIT(n);
276324 }
325
+ spin_unlock_irqrestore(&bigben->lock, flags);
277326
278327 if (work) {
279328 bigben->work_led = true;
280
- schedule_work(&bigben->worker);
329
+ bigben_schedule_work(bigben);
281330 }
282331 return;
283332 }
....@@ -307,8 +356,12 @@
307356 static void bigben_remove(struct hid_device *hid)
308357 {
309358 struct bigben_device *bigben = hid_get_drvdata(hid);
359
+ unsigned long flags;
310360
361
+ spin_lock_irqsave(&bigben->lock, flags);
311362 bigben->removed = true;
363
+ spin_unlock_irqrestore(&bigben->lock, flags);
364
+
312365 cancel_work_sync(&bigben->worker);
313366 hid_hw_stop(hid);
314367 }
....@@ -318,7 +371,6 @@
318371 {
319372 struct bigben_device *bigben;
320373 struct hid_input *hidinput;
321
- struct list_head *report_list;
322374 struct led_classdev *led;
323375 char *name;
324376 size_t name_sz;
....@@ -343,9 +395,12 @@
343395 return error;
344396 }
345397
346
- report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
347
- bigben->report = list_entry(report_list->next,
348
- struct hid_report, list);
398
+ bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8);
399
+ if (!bigben->report) {
400
+ hid_err(hid, "no output report found\n");
401
+ error = -ENODEV;
402
+ goto error_hw_stop;
403
+ }
349404
350405 if (list_empty(&hid->inputs)) {
351406 hid_err(hid, "no inputs found\n");
....@@ -357,6 +412,7 @@
357412 set_bit(FF_RUMBLE, hidinput->input->ffbit);
358413
359414 INIT_WORK(&bigben->worker, bigben_worker);
415
+ spin_lock_init(&bigben->lock);
360416
361417 error = input_ff_create_memless(hidinput->input, NULL,
362418 hid_bigben_play_effect);
....@@ -397,7 +453,7 @@
397453 bigben->left_motor_force = 0;
398454 bigben->work_led = true;
399455 bigben->work_ff = true;
400
- schedule_work(&bigben->worker);
456
+ bigben_schedule_work(bigben);
401457
402458 hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
403459