1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/atomic.h>
14#include <linux/module.h>
15#include <linux/spinlock.h>
16#include <linux/workqueue.h>
17#include "hid-wiimote.h"
18
19struct wiimote_ext {
20 struct wiimote_data *wdata;
21 struct work_struct worker;
22 struct input_dev *input;
23 struct input_dev *mp_input;
24
25 atomic_t opened;
26 atomic_t mp_opened;
27 bool plugged;
28 bool mp_plugged;
29 bool motionp;
30 __u8 ext_type;
31 __u16 calib[4][3];
32};
33
34enum wiiext_type {
35 WIIEXT_NONE,
36 WIIEXT_CLASSIC,
37 WIIEXT_NUNCHUCK,
38 WIIEXT_BALANCE_BOARD,
39};
40
41enum wiiext_keys {
42 WIIEXT_KEY_C,
43 WIIEXT_KEY_Z,
44 WIIEXT_KEY_A,
45 WIIEXT_KEY_B,
46 WIIEXT_KEY_X,
47 WIIEXT_KEY_Y,
48 WIIEXT_KEY_ZL,
49 WIIEXT_KEY_ZR,
50 WIIEXT_KEY_PLUS,
51 WIIEXT_KEY_MINUS,
52 WIIEXT_KEY_HOME,
53 WIIEXT_KEY_LEFT,
54 WIIEXT_KEY_RIGHT,
55 WIIEXT_KEY_UP,
56 WIIEXT_KEY_DOWN,
57 WIIEXT_KEY_LT,
58 WIIEXT_KEY_RT,
59 WIIEXT_KEY_COUNT
60};
61
62static __u16 wiiext_keymap[] = {
63 BTN_C,
64 BTN_Z,
65 BTN_A,
66 BTN_B,
67 BTN_X,
68 BTN_Y,
69 BTN_TL2,
70 BTN_TR2,
71 KEY_NEXT,
72 KEY_PREVIOUS,
73 BTN_MODE,
74 KEY_LEFT,
75 KEY_RIGHT,
76 KEY_UP,
77 KEY_DOWN,
78 BTN_TL,
79 BTN_TR,
80};
81
82
83static void ext_disable(struct wiimote_ext *ext)
84{
85 unsigned long flags;
86 __u8 wmem = 0x55;
87
88 if (!wiimote_cmd_acquire(ext->wdata)) {
89 wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
90 wiimote_cmd_release(ext->wdata);
91 }
92
93 spin_lock_irqsave(&ext->wdata->state.lock, flags);
94 ext->motionp = false;
95 ext->ext_type = WIIEXT_NONE;
96 wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
97 spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
98}
99
100static bool motionp_read(struct wiimote_ext *ext)
101{
102 __u8 rmem[2], wmem;
103 ssize_t ret;
104 bool avail = false;
105
106 if (!atomic_read(&ext->mp_opened))
107 return false;
108
109 if (wiimote_cmd_acquire(ext->wdata))
110 return false;
111
112
113 wmem = 0x55;
114 ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem));
115 if (ret)
116 goto error;
117
118
119 ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2);
120 if (ret == 2 || rmem[1] == 0x5)
121 avail = true;
122
123error:
124 wiimote_cmd_release(ext->wdata);
125 return avail;
126}
127
128static __u8 ext_read(struct wiimote_ext *ext)
129{
130 ssize_t ret;
131 __u8 buf[24], i, j, offs = 0;
132 __u8 rmem[2], wmem;
133 __u8 type = WIIEXT_NONE;
134
135 if (!ext->plugged || !atomic_read(&ext->opened))
136 return WIIEXT_NONE;
137
138 if (wiimote_cmd_acquire(ext->wdata))
139 return WIIEXT_NONE;
140
141
142 wmem = 0x55;
143 ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
144 if (!ret) {
145
146 wmem = 0x0;
147 wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem));
148 }
149
150
151 ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2);
152 if (ret == 2) {
153 if (rmem[0] == 0 && rmem[1] == 0)
154 type = WIIEXT_NUNCHUCK;
155 else if (rmem[0] == 0x01 && rmem[1] == 0x01)
156 type = WIIEXT_CLASSIC;
157 else if (rmem[0] == 0x04 && rmem[1] == 0x02)
158 type = WIIEXT_BALANCE_BOARD;
159 }
160
161
162 if (type == WIIEXT_BALANCE_BOARD) {
163 ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12);
164 ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12,
165 buf + 12, 12);
166
167 if (ret != 24) {
168 type = WIIEXT_NONE;
169 } else {
170 for (i = 0; i < 3; i++) {
171 for (j = 0; j < 4; j++) {
172 ext->calib[j][i] = buf[offs];
173 ext->calib[j][i] <<= 8;
174 ext->calib[j][i] |= buf[offs + 1];
175 offs += 2;
176 }
177 }
178 }
179 }
180
181 wiimote_cmd_release(ext->wdata);
182
183 return type;
184}
185
186static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type)
187{
188 unsigned long flags;
189 __u8 wmem;
190 int ret;
191
192 if (motionp) {
193 if (wiimote_cmd_acquire(ext->wdata))
194 return;
195
196 if (ext_type == WIIEXT_CLASSIC)
197 wmem = 0x07;
198 else if (ext_type == WIIEXT_NUNCHUCK)
199 wmem = 0x05;
200 else
201 wmem = 0x04;
202
203 ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem));
204 wiimote_cmd_release(ext->wdata);
205 if (ret)
206 return;
207 }
208
209 spin_lock_irqsave(&ext->wdata->state.lock, flags);
210 ext->motionp = motionp;
211 ext->ext_type = ext_type;
212 wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
213 spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
214}
215
216static void wiiext_worker(struct work_struct *work)
217{
218 struct wiimote_ext *ext = container_of(work, struct wiimote_ext,
219 worker);
220 bool motionp;
221 __u8 ext_type;
222
223 ext_disable(ext);
224 motionp = motionp_read(ext);
225 ext_type = ext_read(ext);
226 ext_enable(ext, motionp, ext_type);
227}
228
229
230static void wiiext_schedule(struct wiimote_ext *ext)
231{
232 schedule_work(&ext->worker);
233}
234
235
236
237
238
239
240
241
242
243void wiiext_event(struct wiimote_data *wdata, bool plugged)
244{
245 if (!wdata->ext)
246 return;
247
248 if (wdata->ext->plugged == plugged)
249 return;
250
251 wdata->ext->plugged = plugged;
252
253 if (!plugged)
254 wdata->ext->mp_plugged = false;
255
256
257
258
259
260
261}
262
263
264
265
266
267
268
269
270bool wiiext_active(struct wiimote_data *wdata)
271{
272 if (!wdata->ext)
273 return false;
274
275 return wdata->ext->motionp || wdata->ext->ext_type;
276}
277
278static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload)
279{
280 __s32 x, y, z;
281 bool plugged;
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304 x = payload[0];
305 y = payload[1];
306 z = payload[2];
307
308 x |= (((__u16)payload[3]) << 6) & 0xff00;
309 y |= (((__u16)payload[4]) << 6) & 0xff00;
310 z |= (((__u16)payload[5]) << 6) & 0xff00;
311
312 x -= 8192;
313 y -= 8192;
314 z -= 8192;
315
316 if (!(payload[3] & 0x02))
317 x *= 18;
318 else
319 x *= 9;
320 if (!(payload[4] & 0x02))
321 y *= 18;
322 else
323 y *= 9;
324 if (!(payload[3] & 0x01))
325 z *= 18;
326 else
327 z *= 9;
328
329 input_report_abs(ext->mp_input, ABS_RX, x);
330 input_report_abs(ext->mp_input, ABS_RY, y);
331 input_report_abs(ext->mp_input, ABS_RZ, z);
332 input_sync(ext->mp_input);
333
334 plugged = payload[5] & 0x01;
335 if (plugged != ext->mp_plugged)
336 ext->mp_plugged = plugged;
337}
338
339static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload)
340{
341 __s16 x, y, z, bx, by;
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 bx = payload[0];
374 by = payload[1];
375 bx -= 128;
376 by -= 128;
377
378 x = payload[2] << 2;
379 y = payload[3] << 2;
380 z = payload[4] << 2;
381
382 if (ext->motionp) {
383 x |= (payload[5] >> 3) & 0x02;
384 y |= (payload[5] >> 4) & 0x02;
385 z &= ~0x4;
386 z |= (payload[5] >> 5) & 0x06;
387 } else {
388 x |= (payload[5] >> 2) & 0x03;
389 y |= (payload[5] >> 4) & 0x03;
390 z |= (payload[5] >> 6) & 0x03;
391 }
392
393 x -= 0x200;
394 y -= 0x200;
395 z -= 0x200;
396
397 input_report_abs(ext->input, ABS_HAT0X, bx);
398 input_report_abs(ext->input, ABS_HAT0Y, by);
399
400 input_report_abs(ext->input, ABS_RX, x);
401 input_report_abs(ext->input, ABS_RY, y);
402 input_report_abs(ext->input, ABS_RZ, z);
403
404 if (ext->motionp) {
405 input_report_key(ext->input,
406 wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x04));
407 input_report_key(ext->input,
408 wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x08));
409 } else {
410 input_report_key(ext->input,
411 wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x01));
412 input_report_key(ext->input,
413 wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x02));
414 }
415
416 input_sync(ext->input);
417}
418
419static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
420{
421 __s8 rx, ry, lx, ly, lt, rt;
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 if (ext->motionp) {
467 lx = payload[0] & 0x3e;
468 ly = payload[0] & 0x3e;
469 } else {
470 lx = payload[0] & 0x3f;
471 ly = payload[0] & 0x3f;
472 }
473
474 rx = (payload[0] >> 3) & 0x14;
475 rx |= (payload[1] >> 5) & 0x06;
476 rx |= (payload[2] >> 7) & 0x01;
477 ry = payload[2] & 0x1f;
478
479 rt = payload[3] & 0x1f;
480 lt = (payload[2] >> 2) & 0x18;
481 lt |= (payload[3] >> 5) & 0x07;
482
483 rx <<= 1;
484 ry <<= 1;
485 rt <<= 1;
486 lt <<= 1;
487
488 input_report_abs(ext->input, ABS_HAT1X, lx - 0x20);
489 input_report_abs(ext->input, ABS_HAT1Y, ly - 0x20);
490 input_report_abs(ext->input, ABS_HAT2X, rx - 0x20);
491 input_report_abs(ext->input, ABS_HAT2Y, ry - 0x20);
492 input_report_abs(ext->input, ABS_HAT3X, rt - 0x20);
493 input_report_abs(ext->input, ABS_HAT3Y, lt - 0x20);
494
495 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RIGHT],
496 !!(payload[4] & 0x80));
497 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_DOWN],
498 !!(payload[4] & 0x40));
499 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LT],
500 !!(payload[4] & 0x20));
501 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_MINUS],
502 !!(payload[4] & 0x10));
503 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_HOME],
504 !!(payload[4] & 0x08));
505 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_PLUS],
506 !!(payload[4] & 0x04));
507 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RT],
508 !!(payload[4] & 0x02));
509 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZL],
510 !!(payload[5] & 0x80));
511 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_B],
512 !!(payload[5] & 0x40));
513 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_Y],
514 !!(payload[5] & 0x20));
515 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_A],
516 !!(payload[5] & 0x10));
517 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_X],
518 !!(payload[5] & 0x08));
519 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZR],
520 !!(payload[5] & 0x04));
521
522 if (ext->motionp) {
523 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
524 !!(payload[0] & 0x01));
525 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
526 !!(payload[1] & 0x01));
527 } else {
528 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
529 !!(payload[5] & 0x01));
530 input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
531 !!(payload[5] & 0x02));
532 }
533
534 input_sync(ext->input);
535}
536
537static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload)
538{
539 __s32 val[4], tmp;
540 unsigned int i;
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563 val[0] = payload[0];
564 val[0] <<= 8;
565 val[0] |= payload[1];
566
567 val[1] = payload[2];
568 val[1] <<= 8;
569 val[1] |= payload[3];
570
571 val[2] = payload[4];
572 val[2] <<= 8;
573 val[2] |= payload[5];
574
575 val[3] = payload[6];
576 val[3] <<= 8;
577 val[3] |= payload[7];
578
579
580 for (i = 0; i < 4; i++) {
581 if (val[i] < ext->calib[i][1]) {
582 tmp = val[i] - ext->calib[i][0];
583 tmp *= 1700;
584 tmp /= ext->calib[i][1] - ext->calib[i][0];
585 } else {
586 tmp = val[i] - ext->calib[i][1];
587 tmp *= 1700;
588 tmp /= ext->calib[i][2] - ext->calib[i][1];
589 tmp += 1700;
590 }
591 val[i] = tmp;
592 }
593
594 input_report_abs(ext->input, ABS_HAT0X, val[0]);
595 input_report_abs(ext->input, ABS_HAT0Y, val[1]);
596 input_report_abs(ext->input, ABS_HAT1X, val[2]);
597 input_report_abs(ext->input, ABS_HAT1Y, val[3]);
598
599 input_sync(ext->input);
600}
601
602
603void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
604{
605 struct wiimote_ext *ext = wdata->ext;
606
607 if (!ext)
608 return;
609
610 if (ext->motionp && (payload[5] & 0x02)) {
611 handler_motionp(ext, payload);
612 } else if (ext->ext_type == WIIEXT_NUNCHUCK) {
613 handler_nunchuck(ext, payload);
614 } else if (ext->ext_type == WIIEXT_CLASSIC) {
615 handler_classic(ext, payload);
616 } else if (ext->ext_type == WIIEXT_BALANCE_BOARD) {
617 handler_balance_board(ext, payload);
618 }
619}
620
621static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
622 char *buf)
623{
624 struct wiimote_data *wdata = dev_to_wii(dev);
625 __u8 type = WIIEXT_NONE;
626 bool motionp = false;
627 unsigned long flags;
628
629 spin_lock_irqsave(&wdata->state.lock, flags);
630 if (wdata->ext) {
631 motionp = wdata->ext->motionp;
632 type = wdata->ext->ext_type;
633 }
634 spin_unlock_irqrestore(&wdata->state.lock, flags);
635
636 if (type == WIIEXT_NUNCHUCK) {
637 if (motionp)
638 return sprintf(buf, "motionp+nunchuck\n");
639 else
640 return sprintf(buf, "nunchuck\n");
641 } else if (type == WIIEXT_CLASSIC) {
642 if (motionp)
643 return sprintf(buf, "motionp+classic\n");
644 else
645 return sprintf(buf, "classic\n");
646 } else if (type == WIIEXT_BALANCE_BOARD) {
647 if (motionp)
648 return sprintf(buf, "motionp+balanceboard\n");
649 else
650 return sprintf(buf, "balanceboard\n");
651 } else {
652 if (motionp)
653 return sprintf(buf, "motionp\n");
654 else
655 return sprintf(buf, "none\n");
656 }
657}
658
659static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL);
660
661static int wiiext_input_open(struct input_dev *dev)
662{
663 struct wiimote_ext *ext = input_get_drvdata(dev);
664 int ret;
665
666 ret = hid_hw_open(ext->wdata->hdev);
667 if (ret)
668 return ret;
669
670 atomic_inc(&ext->opened);
671 wiiext_schedule(ext);
672
673 return 0;
674}
675
676static void wiiext_input_close(struct input_dev *dev)
677{
678 struct wiimote_ext *ext = input_get_drvdata(dev);
679
680 atomic_dec(&ext->opened);
681 wiiext_schedule(ext);
682 hid_hw_close(ext->wdata->hdev);
683}
684
685static int wiiext_mp_open(struct input_dev *dev)
686{
687 struct wiimote_ext *ext = input_get_drvdata(dev);
688 int ret;
689
690 ret = hid_hw_open(ext->wdata->hdev);
691 if (ret)
692 return ret;
693
694 atomic_inc(&ext->mp_opened);
695 wiiext_schedule(ext);
696
697 return 0;
698}
699
700static void wiiext_mp_close(struct input_dev *dev)
701{
702 struct wiimote_ext *ext = input_get_drvdata(dev);
703
704 atomic_dec(&ext->mp_opened);
705 wiiext_schedule(ext);
706 hid_hw_close(ext->wdata->hdev);
707}
708
709
710int wiiext_init(struct wiimote_data *wdata)
711{
712 struct wiimote_ext *ext;
713 unsigned long flags;
714 int ret, i;
715
716 ext = kzalloc(sizeof(*ext), GFP_KERNEL);
717 if (!ext)
718 return -ENOMEM;
719
720 ext->wdata = wdata;
721 INIT_WORK(&ext->worker, wiiext_worker);
722
723 ext->input = input_allocate_device();
724 if (!ext->input) {
725 ret = -ENOMEM;
726 goto err_input;
727 }
728
729 input_set_drvdata(ext->input, ext);
730 ext->input->open = wiiext_input_open;
731 ext->input->close = wiiext_input_close;
732 ext->input->dev.parent = &wdata->hdev->dev;
733 ext->input->id.bustype = wdata->hdev->bus;
734 ext->input->id.vendor = wdata->hdev->vendor;
735 ext->input->id.product = wdata->hdev->product;
736 ext->input->id.version = wdata->hdev->version;
737 ext->input->name = WIIMOTE_NAME " Extension";
738
739 set_bit(EV_KEY, ext->input->evbit);
740 for (i = 0; i < WIIEXT_KEY_COUNT; ++i)
741 set_bit(wiiext_keymap[i], ext->input->keybit);
742
743 set_bit(EV_ABS, ext->input->evbit);
744 set_bit(ABS_HAT0X, ext->input->absbit);
745 set_bit(ABS_HAT0Y, ext->input->absbit);
746 set_bit(ABS_HAT1X, ext->input->absbit);
747 set_bit(ABS_HAT1Y, ext->input->absbit);
748 set_bit(ABS_HAT2X, ext->input->absbit);
749 set_bit(ABS_HAT2Y, ext->input->absbit);
750 set_bit(ABS_HAT3X, ext->input->absbit);
751 set_bit(ABS_HAT3Y, ext->input->absbit);
752 input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4);
753 input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4);
754 input_set_abs_params(ext->input, ABS_HAT1X, -30, 30, 1, 1);
755 input_set_abs_params(ext->input, ABS_HAT1Y, -30, 30, 1, 1);
756 input_set_abs_params(ext->input, ABS_HAT2X, -30, 30, 1, 1);
757 input_set_abs_params(ext->input, ABS_HAT2Y, -30, 30, 1, 1);
758 input_set_abs_params(ext->input, ABS_HAT3X, -30, 30, 1, 1);
759 input_set_abs_params(ext->input, ABS_HAT3Y, -30, 30, 1, 1);
760 set_bit(ABS_RX, ext->input->absbit);
761 set_bit(ABS_RY, ext->input->absbit);
762 set_bit(ABS_RZ, ext->input->absbit);
763 input_set_abs_params(ext->input, ABS_RX, -500, 500, 2, 4);
764 input_set_abs_params(ext->input, ABS_RY, -500, 500, 2, 4);
765 input_set_abs_params(ext->input, ABS_RZ, -500, 500, 2, 4);
766
767 ret = input_register_device(ext->input);
768 if (ret) {
769 input_free_device(ext->input);
770 goto err_input;
771 }
772
773 ext->mp_input = input_allocate_device();
774 if (!ext->mp_input) {
775 ret = -ENOMEM;
776 goto err_mp;
777 }
778
779 input_set_drvdata(ext->mp_input, ext);
780 ext->mp_input->open = wiiext_mp_open;
781 ext->mp_input->close = wiiext_mp_close;
782 ext->mp_input->dev.parent = &wdata->hdev->dev;
783 ext->mp_input->id.bustype = wdata->hdev->bus;
784 ext->mp_input->id.vendor = wdata->hdev->vendor;
785 ext->mp_input->id.product = wdata->hdev->product;
786 ext->mp_input->id.version = wdata->hdev->version;
787 ext->mp_input->name = WIIMOTE_NAME " Motion+";
788
789 set_bit(EV_ABS, ext->mp_input->evbit);
790 set_bit(ABS_RX, ext->mp_input->absbit);
791 set_bit(ABS_RY, ext->mp_input->absbit);
792 set_bit(ABS_RZ, ext->mp_input->absbit);
793 input_set_abs_params(ext->mp_input, ABS_RX, -160000, 160000, 4, 8);
794 input_set_abs_params(ext->mp_input, ABS_RY, -160000, 160000, 4, 8);
795 input_set_abs_params(ext->mp_input, ABS_RZ, -160000, 160000, 4, 8);
796
797 ret = input_register_device(ext->mp_input);
798 if (ret) {
799 input_free_device(ext->mp_input);
800 goto err_mp;
801 }
802
803 ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension);
804 if (ret)
805 goto err_dev;
806
807 spin_lock_irqsave(&wdata->state.lock, flags);
808 wdata->ext = ext;
809 spin_unlock_irqrestore(&wdata->state.lock, flags);
810
811 return 0;
812
813err_dev:
814 input_unregister_device(ext->mp_input);
815err_mp:
816 input_unregister_device(ext->input);
817err_input:
818 kfree(ext);
819 return ret;
820}
821
822
823void wiiext_deinit(struct wiimote_data *wdata)
824{
825 struct wiimote_ext *ext = wdata->ext;
826 unsigned long flags;
827
828 if (!ext)
829 return;
830
831
832
833
834
835
836
837
838
839 spin_lock_irqsave(&wdata->state.lock, flags);
840 wdata->ext = NULL;
841 spin_unlock_irqrestore(&wdata->state.lock, flags);
842
843 device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
844 input_unregister_device(ext->mp_input);
845 input_unregister_device(ext->input);
846
847 cancel_work_sync(&ext->worker);
848 kfree(ext);
849}
850