@@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = {
174
174
struct bigben_device {
175
175
struct hid_device * hid ;
176
176
struct hid_report * report ;
177
+ spinlock_t lock ;
177
178
bool removed ;
178
179
u8 led_state ; /* LED1 = 1 .. LED4 = 8 */
179
180
u8 right_motor_on ; /* right motor off/on 0/1 */
@@ -184,18 +185,39 @@ struct bigben_device {
184
185
struct work_struct worker ;
185
186
};
186
187
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
+ }
187
197
188
198
static void bigben_worker (struct work_struct * work )
189
199
{
190
200
struct bigben_device * bigben = container_of (work ,
191
201
struct bigben_device , worker );
192
202
struct hid_field * report_field = bigben -> report -> field [0 ];
193
-
194
- if (bigben -> removed || !report_field )
203
+ bool do_work_led = false;
204
+ bool do_work_ff = false;
205
+ u8 * buf ;
206
+ u32 len ;
207
+ unsigned long flags ;
208
+
209
+ buf = hid_alloc_report_buf (bigben -> report , GFP_KERNEL );
210
+ if (!buf )
195
211
return ;
196
212
213
+ len = hid_report_len (bigben -> report );
214
+
215
+ /* LED work */
216
+ spin_lock_irqsave (& bigben -> lock , flags );
217
+
197
218
if (bigben -> work_led ) {
198
219
bigben -> work_led = false;
220
+ do_work_led = true;
199
221
report_field -> value [0 ] = 0x01 ; /* 1 = led message */
200
222
report_field -> value [1 ] = 0x08 ; /* reserved value, always 8 */
201
223
report_field -> value [2 ] = bigben -> led_state ;
@@ -204,11 +226,22 @@ static void bigben_worker(struct work_struct *work)
204
226
report_field -> value [5 ] = 0x00 ; /* padding */
205
227
report_field -> value [6 ] = 0x00 ; /* padding */
206
228
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 );
230
+ }
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 );
208
237
}
209
238
239
+ /* FF work */
240
+ spin_lock_irqsave (& bigben -> lock , flags );
241
+
210
242
if (bigben -> work_ff ) {
211
243
bigben -> work_ff = false;
244
+ do_work_ff = true;
212
245
report_field -> value [0 ] = 0x02 ; /* 2 = rumble effect message */
213
246
report_field -> value [1 ] = 0x08 ; /* reserved value, always 8 */
214
247
report_field -> value [2 ] = bigben -> right_motor_on ;
@@ -217,8 +250,17 @@ static void bigben_worker(struct work_struct *work)
217
250
report_field -> value [5 ] = 0x00 ; /* padding */
218
251
report_field -> value [6 ] = 0x00 ; /* padding */
219
252
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 );
254
+ }
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 );
221
261
}
262
+
263
+ kfree (buf );
222
264
}
223
265
224
266
static int hid_bigben_play_effect (struct input_dev * dev , void * data ,
@@ -228,6 +270,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
228
270
struct bigben_device * bigben = hid_get_drvdata (hid );
229
271
u8 right_motor_on ;
230
272
u8 left_motor_force ;
273
+ unsigned long flags ;
231
274
232
275
if (!bigben ) {
233
276
hid_err (hid , "no device data\n" );
@@ -242,10 +285,13 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
242
285
243
286
if (right_motor_on != bigben -> right_motor_on ||
244
287
left_motor_force != bigben -> left_motor_force ) {
288
+ spin_lock_irqsave (& bigben -> lock , flags );
245
289
bigben -> right_motor_on = right_motor_on ;
246
290
bigben -> left_motor_force = left_motor_force ;
247
291
bigben -> work_ff = true;
248
- schedule_work (& bigben -> worker );
292
+ spin_unlock_irqrestore (& bigben -> lock , flags );
293
+
294
+ bigben_schedule_work (bigben );
249
295
}
250
296
251
297
return 0 ;
@@ -259,6 +305,7 @@ static void bigben_set_led(struct led_classdev *led,
259
305
struct bigben_device * bigben = hid_get_drvdata (hid );
260
306
int n ;
261
307
bool work ;
308
+ unsigned long flags ;
262
309
263
310
if (!bigben ) {
264
311
hid_err (hid , "no device data\n" );
@@ -267,17 +314,19 @@ static void bigben_set_led(struct led_classdev *led,
267
314
268
315
for (n = 0 ; n < NUM_LEDS ; n ++ ) {
269
316
if (led == bigben -> leds [n ]) {
317
+ spin_lock_irqsave (& bigben -> lock , flags );
270
318
if (value == LED_OFF ) {
271
319
work = (bigben -> led_state & BIT (n ));
272
320
bigben -> led_state &= ~BIT (n );
273
321
} else {
274
322
work = !(bigben -> led_state & BIT (n ));
275
323
bigben -> led_state |= BIT (n );
276
324
}
325
+ spin_unlock_irqrestore (& bigben -> lock , flags );
277
326
278
327
if (work ) {
279
328
bigben -> work_led = true;
280
- schedule_work ( & bigben -> worker );
329
+ bigben_schedule_work ( bigben );
281
330
}
282
331
return ;
283
332
}
@@ -307,8 +356,12 @@ static enum led_brightness bigben_get_led(struct led_classdev *led)
307
356
static void bigben_remove (struct hid_device * hid )
308
357
{
309
358
struct bigben_device * bigben = hid_get_drvdata (hid );
359
+ unsigned long flags ;
310
360
361
+ spin_lock_irqsave (& bigben -> lock , flags );
311
362
bigben -> removed = true;
363
+ spin_unlock_irqrestore (& bigben -> lock , flags );
364
+
312
365
cancel_work_sync (& bigben -> worker );
313
366
hid_hw_stop (hid );
314
367
}
@@ -318,7 +371,6 @@ static int bigben_probe(struct hid_device *hid,
318
371
{
319
372
struct bigben_device * bigben ;
320
373
struct hid_input * hidinput ;
321
- struct list_head * report_list ;
322
374
struct led_classdev * led ;
323
375
char * name ;
324
376
size_t name_sz ;
@@ -343,14 +395,12 @@ static int bigben_probe(struct hid_device *hid,
343
395
return error ;
344
396
}
345
397
346
- report_list = & hid -> report_enum [ HID_OUTPUT_REPORT ]. report_list ;
347
- if (list_empty ( report_list ) ) {
398
+ bigben -> report = hid_validate_values ( hid , HID_OUTPUT_REPORT , 0 , 0 , 8 ) ;
399
+ if (! bigben -> report ) {
348
400
hid_err (hid , "no output report found\n" );
349
401
error = - ENODEV ;
350
402
goto error_hw_stop ;
351
403
}
352
- bigben -> report = list_entry (report_list -> next ,
353
- struct hid_report , list );
354
404
355
405
if (list_empty (& hid -> inputs )) {
356
406
hid_err (hid , "no inputs found\n" );
@@ -362,6 +412,7 @@ static int bigben_probe(struct hid_device *hid,
362
412
set_bit (FF_RUMBLE , hidinput -> input -> ffbit );
363
413
364
414
INIT_WORK (& bigben -> worker , bigben_worker );
415
+ spin_lock_init (& bigben -> lock );
365
416
366
417
error = input_ff_create_memless (hidinput -> input , NULL ,
367
418
hid_bigben_play_effect );
@@ -402,7 +453,7 @@ static int bigben_probe(struct hid_device *hid,
402
453
bigben -> left_motor_force = 0 ;
403
454
bigben -> work_led = true;
404
455
bigben -> work_ff = true;
405
- schedule_work ( & bigben -> worker );
456
+ bigben_schedule_work ( bigben );
406
457
407
458
hid_info (hid , "LED and force feedback support for BigBen gamepad\n" );
408
459
0 commit comments