diff options
Diffstat (limited to 'drivers/hid/hid-wiimote-modules.c')
-rw-r--r-- | drivers/hid/hid-wiimote-modules.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 4cbbbe651ba5..f96de153971c 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c | |||
@@ -268,10 +268,150 @@ static const struct wiimod_ops wiimod_battery = { | |||
268 | .remove = wiimod_battery_remove, | 268 | .remove = wiimod_battery_remove, |
269 | }; | 269 | }; |
270 | 270 | ||
271 | /* | ||
272 | * LED | ||
273 | * 0 to 4 player LEDs are supported by devices. The "arg" field of the | ||
274 | * wiimod_ops structure specifies which LED this module controls. This allows | ||
275 | * to register a limited number of LEDs. | ||
276 | * State is managed by wiimote core. | ||
277 | */ | ||
278 | |||
279 | static enum led_brightness wiimod_led_get(struct led_classdev *led_dev) | ||
280 | { | ||
281 | struct wiimote_data *wdata; | ||
282 | struct device *dev = led_dev->dev->parent; | ||
283 | int i; | ||
284 | unsigned long flags; | ||
285 | bool value = false; | ||
286 | |||
287 | wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); | ||
288 | |||
289 | for (i = 0; i < 4; ++i) { | ||
290 | if (wdata->leds[i] == led_dev) { | ||
291 | spin_lock_irqsave(&wdata->state.lock, flags); | ||
292 | value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1); | ||
293 | spin_unlock_irqrestore(&wdata->state.lock, flags); | ||
294 | break; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | return value ? LED_FULL : LED_OFF; | ||
299 | } | ||
300 | |||
301 | static void wiimod_led_set(struct led_classdev *led_dev, | ||
302 | enum led_brightness value) | ||
303 | { | ||
304 | struct wiimote_data *wdata; | ||
305 | struct device *dev = led_dev->dev->parent; | ||
306 | int i; | ||
307 | unsigned long flags; | ||
308 | __u8 state, flag; | ||
309 | |||
310 | wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); | ||
311 | |||
312 | for (i = 0; i < 4; ++i) { | ||
313 | if (wdata->leds[i] == led_dev) { | ||
314 | flag = WIIPROTO_FLAG_LED(i + 1); | ||
315 | spin_lock_irqsave(&wdata->state.lock, flags); | ||
316 | state = wdata->state.flags; | ||
317 | if (value == LED_OFF) | ||
318 | wiiproto_req_leds(wdata, state & ~flag); | ||
319 | else | ||
320 | wiiproto_req_leds(wdata, state | flag); | ||
321 | spin_unlock_irqrestore(&wdata->state.lock, flags); | ||
322 | break; | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | |||
327 | static int wiimod_led_probe(const struct wiimod_ops *ops, | ||
328 | struct wiimote_data *wdata) | ||
329 | { | ||
330 | struct device *dev = &wdata->hdev->dev; | ||
331 | size_t namesz = strlen(dev_name(dev)) + 9; | ||
332 | struct led_classdev *led; | ||
333 | unsigned long flags; | ||
334 | char *name; | ||
335 | int ret; | ||
336 | |||
337 | led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); | ||
338 | if (!led) | ||
339 | return -ENOMEM; | ||
340 | |||
341 | name = (void*)&led[1]; | ||
342 | snprintf(name, namesz, "%s:blue:p%lu", dev_name(dev), ops->arg); | ||
343 | led->name = name; | ||
344 | led->brightness = 0; | ||
345 | led->max_brightness = 1; | ||
346 | led->brightness_get = wiimod_led_get; | ||
347 | led->brightness_set = wiimod_led_set; | ||
348 | |||
349 | wdata->leds[ops->arg] = led; | ||
350 | ret = led_classdev_register(dev, led); | ||
351 | if (ret) | ||
352 | goto err_free; | ||
353 | |||
354 | /* enable LED1 to stop initial LED-blinking */ | ||
355 | if (ops->arg == 0) { | ||
356 | spin_lock_irqsave(&wdata->state.lock, flags); | ||
357 | wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1); | ||
358 | spin_unlock_irqrestore(&wdata->state.lock, flags); | ||
359 | } | ||
360 | |||
361 | return 0; | ||
362 | |||
363 | err_free: | ||
364 | wdata->leds[ops->arg] = NULL; | ||
365 | kfree(led); | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | static void wiimod_led_remove(const struct wiimod_ops *ops, | ||
370 | struct wiimote_data *wdata) | ||
371 | { | ||
372 | if (!wdata->leds[ops->arg]) | ||
373 | return; | ||
374 | |||
375 | led_classdev_unregister(wdata->leds[ops->arg]); | ||
376 | kfree(wdata->leds[ops->arg]); | ||
377 | wdata->leds[ops->arg] = NULL; | ||
378 | } | ||
379 | |||
380 | static const struct wiimod_ops wiimod_leds[4] = { | ||
381 | { | ||
382 | .flags = 0, | ||
383 | .arg = 0, | ||
384 | .probe = wiimod_led_probe, | ||
385 | .remove = wiimod_led_remove, | ||
386 | }, | ||
387 | { | ||
388 | .flags = 0, | ||
389 | .arg = 1, | ||
390 | .probe = wiimod_led_probe, | ||
391 | .remove = wiimod_led_remove, | ||
392 | }, | ||
393 | { | ||
394 | .flags = 0, | ||
395 | .arg = 2, | ||
396 | .probe = wiimod_led_probe, | ||
397 | .remove = wiimod_led_remove, | ||
398 | }, | ||
399 | { | ||
400 | .flags = 0, | ||
401 | .arg = 3, | ||
402 | .probe = wiimod_led_probe, | ||
403 | .remove = wiimod_led_remove, | ||
404 | }, | ||
405 | }; | ||
406 | |||
271 | /* module table */ | 407 | /* module table */ |
272 | 408 | ||
273 | const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = { | 409 | const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = { |
274 | [WIIMOD_KEYS] = &wiimod_keys, | 410 | [WIIMOD_KEYS] = &wiimod_keys, |
275 | [WIIMOD_RUMBLE] = &wiimod_rumble, | 411 | [WIIMOD_RUMBLE] = &wiimod_rumble, |
276 | [WIIMOD_BATTERY] = &wiimod_battery, | 412 | [WIIMOD_BATTERY] = &wiimod_battery, |
413 | [WIIMOD_LED1] = &wiimod_leds[0], | ||
414 | [WIIMOD_LED2] = &wiimod_leds[1], | ||
415 | [WIIMOD_LED3] = &wiimod_leds[2], | ||
416 | [WIIMOD_LED4] = &wiimod_leds[3], | ||
277 | }; | 417 | }; |