diff options
-rw-r--r-- | drivers/hid/hid-wiimote-core.c | 669 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote-modules.c | 16 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote.h | 49 |
3 files changed, 709 insertions, 25 deletions
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 1ea70c8..836611e 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -179,17 +179,38 @@ void wiiproto_req_leds(struct wiimote_data *wdata, int leds) * Check what peripherals of the wiimote are currently * active and select a proper DRM that supports all of * the requested data inputs. + * + * Not all combinations are actually supported. The following + * combinations work only with limitations: + * - IR cam in extended or full mode disables any data transmission + * of extension controllers. There is no DRM mode that supports + * extension bytes plus extended/full IR. + * - IR cam with accelerometer and extension *_EXT8 is not supported. + * However, all extensions that need *_EXT8 are devices that don't + * support IR cameras. Hence, this shouldn't happen under normal + * operation. + * - *_EXT16 is only supported in combination with buttons and + * accelerometer. No IR or similar can be active simultaneously. As + * above, all modules that require it are mutually exclusive with + * IR/etc. so this doesn't matter. */ static __u8 select_drm(struct wiimote_data *wdata) { __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR; - bool ext = wiiext_active(wdata); + bool ext; + + ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) || + (wdata->state.flags & WIIPROTO_FLAG_MP_USED); if (ir == WIIPROTO_FLAG_IR_BASIC) { - if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) - return WIIPROTO_REQ_DRM_KAIE; - else + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) { + if (ext) + return WIIPROTO_REQ_DRM_KAIE; + else + return WIIPROTO_REQ_DRM_KAI; + } else { return WIIPROTO_REQ_DRM_KIE; + } } else if (ir == WIIPROTO_FLAG_IR_EXT) { return WIIPROTO_REQ_DRM_KAI; } else if (ir == WIIPROTO_FLAG_IR_FULL) { @@ -202,7 +223,7 @@ static __u8 select_drm(struct wiimote_data *wdata) return WIIPROTO_REQ_DRM_KA; } else { if (ext) - return WIIPROTO_REQ_DRM_KE; + return WIIPROTO_REQ_DRM_KEE; else return WIIPROTO_REQ_DRM_K; } @@ -399,9 +420,8 @@ static int wiimote_cmd_init_ext(struct wiimote_data *wdata) } /* requires the cmd-mutex to be held */ -static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata) +static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem) { - __u8 rmem[6]; int ret; /* read extension ID */ @@ -409,6 +429,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata) if (ret != 6) return WIIMOTE_EXT_NONE; + hid_dbg(wdata->hdev, "extension ID: %02x:%02x %02x:%02x %02x:%02x\n", + rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) return WIIMOTE_EXT_NONE; @@ -416,6 +439,92 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata) return WIIMOTE_EXT_UNKNOWN; } +/* requires the cmd-mutex to be held */ +static int wiimote_cmd_init_mp(struct wiimote_data *wdata) +{ + __u8 wmem; + int ret; + + /* initialize MP */ + wmem = 0x55; + ret = wiimote_cmd_write(wdata, 0xa600f0, &wmem, sizeof(wmem)); + if (ret) + return ret; + + /* disable default encryption */ + wmem = 0x0; + ret = wiimote_cmd_write(wdata, 0xa600fb, &wmem, sizeof(wmem)); + if (ret) + return ret; + + return 0; +} + +/* requires the cmd-mutex to be held */ +static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype) +{ + __u8 wmem; + + /* map MP with correct pass-through mode */ + switch (exttype) { + default: + wmem = 0x04; + break; + } + + return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem)); +} + +/* requires the cmd-mutex to be held */ +static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem) +{ + int ret; + + /* read motion plus ID */ + ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6); + if (ret != 6) + return false; + + hid_dbg(wdata->hdev, "motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n", + rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + + if (rmem[5] == 0x05) + return true; + + hid_info(wdata->hdev, "unknown motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n", + rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + + return false; +} + +/* requires the cmd-mutex to be held */ +static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata) +{ + int ret; + __u8 rmem[6]; + + /* read motion plus ID */ + ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6); + if (ret != 6) + return WIIMOTE_MP_NONE; + + hid_dbg(wdata->hdev, "mapped motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n", + rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]); + + if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && + rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) + return WIIMOTE_MP_NONE; + + if (rmem[4] == 0x04 && rmem[5] == 0x05) + return WIIMOTE_MP_SINGLE; + else if (rmem[4] == 0x05 && rmem[5] == 0x05) + return WIIMOTE_MP_PASSTHROUGH_NUNCHUK; + else if (rmem[4] == 0x07 && rmem[5] == 0x05) + return WIIMOTE_MP_PASSTHROUGH_CLASSIC; + + return WIIMOTE_MP_UNKNOWN; +} + /* device module handling */ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = { @@ -561,6 +670,81 @@ static void wiimote_modules_unload(struct wiimote_data *wdata) } } +/* device extension handling */ + +static void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext) +{ + unsigned long flags; + const struct wiimod_ops *ops; + int ret; + + ops = wiimod_ext_table[ext]; + + if (ops->probe) { + ret = ops->probe(ops, wdata); + if (ret) + ext = WIIMOTE_EXT_UNKNOWN; + } + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.exttype = ext; + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static void wiimote_ext_unload(struct wiimote_data *wdata) +{ + unsigned long flags; + const struct wiimod_ops *ops; + + ops = wiimod_ext_table[wdata->state.exttype]; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.exttype = WIIMOTE_EXT_UNKNOWN; + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + if (ops->remove) + ops->remove(ops, wdata); +} + +static void wiimote_mp_load(struct wiimote_data *wdata) +{ + unsigned long flags; + const struct wiimod_ops *ops; + int ret; + __u8 mode = 2; + + ops = &wiimod_mp; + if (ops->probe) { + ret = ops->probe(ops, wdata); + if (ret) + mode = 1; + } + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.mp = mode; + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static void wiimote_mp_unload(struct wiimote_data *wdata) +{ + unsigned long flags; + const struct wiimod_ops *ops; + + if (wdata->state.mp < 2) + return; + + ops = &wiimod_mp; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.mp = 0; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + if (ops->remove) + ops->remove(ops, wdata); +} + /* device (re-)initialization and detection */ static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = { @@ -617,7 +801,7 @@ done: static void wiimote_init_detect(struct wiimote_data *wdata) { - __u8 exttype = WIIMOTE_EXT_NONE; + __u8 exttype = WIIMOTE_EXT_NONE, extdata[6]; bool ext; int ret; @@ -641,11 +825,301 @@ static void wiimote_init_detect(struct wiimote_data *wdata) goto out_release; wiimote_cmd_init_ext(wdata); - exttype = wiimote_cmd_read_ext(wdata); + exttype = wiimote_cmd_read_ext(wdata, extdata); out_release: wiimote_cmd_release(wdata); wiimote_init_set_type(wdata, exttype); + /* schedule MP timer */ + mod_timer(&wdata->timer, jiffies + HZ * 4); +} + +/* + * MP hotplug events are not generated by the wiimote. Therefore, we need + * polling to detect it. We use a 4s interval for polling MP registers. This + * seems reasonable considering applications can trigger it manually via + * sysfs requests. + */ +static void wiimote_init_poll_mp(struct wiimote_data *wdata) +{ + bool mp; + __u8 mpdata[6]; + + wiimote_cmd_acquire_noint(wdata); + wiimote_cmd_init_mp(wdata); + mp = wiimote_cmd_read_mp(wdata, mpdata); + wiimote_cmd_release(wdata); + + /* load/unload MP module if it changed */ + if (mp) { + if (!wdata->state.mp) { + hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n"); + wiimote_mp_load(wdata); + } + } else if (wdata->state.mp) { + wiimote_mp_unload(wdata); + } + + mod_timer(&wdata->timer, jiffies + HZ * 4); +} + +/* + * Check whether the wiimote is in the expected state. The extension registers + * may change during hotplug and initialization so we might get hotplug events + * that we caused by remapping some memory. + * We use some heuristics here to check known states. If the wiimote is in the + * expected state, we can ignore the hotplug event. + * + * Returns "true" if the device is in expected state, "false" if we should + * redo hotplug handling and extension initialization. + */ +static bool wiimote_init_check(struct wiimote_data *wdata) +{ + __u32 flags; + __u8 type, data[6]; + bool ret, poll_mp; + + spin_lock_irq(&wdata->state.lock); + flags = wdata->state.flags; + spin_unlock_irq(&wdata->state.lock); + + wiimote_cmd_acquire_noint(wdata); + + /* If MP is used and active, but the extension is not, we expect: + * read_mp_mapped() == WIIMOTE_MP_SINGLE + * state.flags == !EXT_ACTIVE && !MP_PLUGGED && MP_ACTIVE + * We do not check EXT_PLUGGED because it might change during + * initialization of MP without extensions. + * - If MP is unplugged/replugged, read_mp_mapped() fails + * - If EXT is plugged, MP_PLUGGED will get set */ + if (wdata->state.exttype == WIIMOTE_EXT_NONE && + wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) { + type = wiimote_cmd_read_mp_mapped(wdata); + ret = type == WIIMOTE_MP_SINGLE; + + spin_lock_irq(&wdata->state.lock); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: !EXT && MP\n"); + + /* while MP is mapped, we get EXT_PLUGGED events */ + poll_mp = false; + + goto out_release; + } + + /* If MP is unused, but the extension port is used, we expect: + * read_ext == state.exttype + * state.flags == !MP_ACTIVE && EXT_ACTIVE + * - If MP is plugged/unplugged, our timer detects it + * - If EXT is unplugged/replugged, EXT_ACTIVE will become unset */ + if (!(flags & WIIPROTO_FLAG_MP_USED) && + wdata->state.exttype != WIIMOTE_EXT_NONE) { + type = wiimote_cmd_read_ext(wdata, data); + ret = type == wdata->state.exttype; + + spin_lock_irq(&wdata->state.lock); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: EXT && !MP\n"); + + /* poll MP for hotplug events */ + poll_mp = true; + + goto out_release; + } + + /* If neither MP nor an extension are used, we expect: + * read_ext() == WIIMOTE_EXT_NONE + * state.flags == !MP_ACTIVE && !EXT_ACTIVE && !EXT_PLUGGED + * No need to perform any action in this case as everything is + * disabled already. + * - If MP is plugged/unplugged, our timer detects it + * - If EXT is plugged, EXT_PLUGGED will be set */ + if (!(flags & WIIPROTO_FLAG_MP_USED) && + wdata->state.exttype == WIIMOTE_EXT_NONE) { + type = wiimote_cmd_read_ext(wdata, data); + ret = type == wdata->state.exttype; + + spin_lock_irq(&wdata->state.lock); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: !EXT && !MP\n"); + + /* poll MP for hotplug events */ + poll_mp = true; + + goto out_release; + } + + /* The trickiest part is if both EXT and MP are active. We cannot read + * the EXT ID, anymore, because MP is mapped over it. However, we use + * a handy trick here: + * - EXT_ACTIVE is unset whenever !MP_PLUGGED is sent + * MP_PLUGGED might be re-sent again before we are scheduled, but + * EXT_ACTIVE will stay unset. + * So it is enough to check for mp_mapped() and MP_ACTIVE and + * EXT_ACTIVE. EXT_PLUGGED is a sanity check. */ + if (wdata->state.exttype != WIIMOTE_EXT_NONE && + wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) { + type = wiimote_cmd_read_mp_mapped(wdata); + ret = type != WIIMOTE_MP_NONE; + ret = ret && type != WIIMOTE_MP_UNKNOWN; + ret = ret && type != WIIMOTE_MP_SINGLE; + + spin_lock_irq(&wdata->state.lock); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: EXT && MP\n"); + + /* while MP is mapped, we get EXT_PLUGGED events */ + poll_mp = false; + + goto out_release; + } + + /* unknown state */ + ret = false; + +out_release: + wiimote_cmd_release(wdata); + + /* only poll for MP if requested and if state didn't change */ + if (ret && poll_mp) + wiimote_init_poll_mp(wdata); + + return ret; +} + +static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = { + [WIIMOTE_EXT_NONE] = "None", + [WIIMOTE_EXT_UNKNOWN] = "Unknown", +}; + +/* + * Handle hotplug events + * If we receive an hotplug event and the device-check failed, we deinitialize + * the extension ports, re-read all extension IDs and set the device into + * the desired state. This involves mapping MP into the main extension + * registers, setting up extension passthrough modes and initializing the + * requested extensions. + */ +static void wiimote_init_hotplug(struct wiimote_data *wdata) +{ + __u8 exttype, extdata[6], mpdata[6]; + __u32 flags; + bool mp; + + hid_dbg(wdata->hdev, "detect extensions..\n"); + + wiimote_cmd_acquire_noint(wdata); + + spin_lock_irq(&wdata->state.lock); + + /* get state snapshot that we will then work on */ + flags = wdata->state.flags; + + /* disable event forwarding temporarily */ + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE; + + spin_unlock_irq(&wdata->state.lock); + + /* init extension and MP (deactivates current extension or MP) */ + wiimote_cmd_init_ext(wdata); + wiimote_cmd_init_mp(wdata); + mp = wiimote_cmd_read_mp(wdata, mpdata); + exttype = wiimote_cmd_read_ext(wdata, extdata); + + wiimote_cmd_release(wdata); + + /* load/unload extension module if it changed */ + if (exttype != wdata->state.exttype) { + /* unload previous extension */ + wiimote_ext_unload(wdata); + + if (exttype == WIIMOTE_EXT_UNKNOWN) { + hid_info(wdata->hdev, "cannot detect extension; %02x:%02x %02x:%02x %02x:%02x\n", + extdata[0], extdata[1], extdata[2], + extdata[3], extdata[4], extdata[5]); + } else if (exttype == WIIMOTE_EXT_NONE) { + spin_lock_irq(&wdata->state.lock); + wdata->state.exttype = WIIMOTE_EXT_NONE; + spin_unlock_irq(&wdata->state.lock); + } else { + hid_info(wdata->hdev, "detected extension: %s\n", + wiimote_exttype_names[exttype]); + /* try loading new extension */ + wiimote_ext_load(wdata, exttype); + } + } + + /* load/unload MP module if it changed */ + if (mp) { + if (!wdata->state.mp) { + hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n"); + wiimote_mp_load(wdata); + } + } else if (wdata->state.mp) { + wiimote_mp_unload(wdata); + } + + /* if MP is not used, do not map or activate it */ + if (!(flags & WIIPROTO_FLAG_MP_USED)) + mp = false; + + /* map MP into main extension registers if used */ + if (mp) { + wiimote_cmd_acquire_noint(wdata); + wiimote_cmd_map_mp(wdata, exttype); + wiimote_cmd_release(wdata); + + /* delete MP hotplug timer */ + del_timer_sync(&wdata->timer); + } else { + /* reschedule MP hotplug timer */ + mod_timer(&wdata->timer, jiffies + HZ * 4); + } + + spin_lock_irq(&wdata->state.lock); + + /* enable data forwarding again and set expected hotplug state */ + if (mp) { + wdata->state.flags |= WIIPROTO_FLAG_MP_ACTIVE; + if (wdata->state.exttype == WIIMOTE_EXT_NONE) { + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED; + } else { + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED; + wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE; + } + } else if (wdata->state.exttype != WIIMOTE_EXT_NONE) { + wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE; + } + + /* request status report for hotplug state updates */ + wiiproto_req_status(wdata); + + spin_unlock_irq(&wdata->state.lock); + + hid_dbg(wdata->hdev, "detected extensions: MP: %d EXT: %d\n", + wdata->state.mp, wdata->state.exttype); } static void wiimote_init_worker(struct work_struct *work) @@ -655,6 +1129,30 @@ static void wiimote_init_worker(struct work_struct *work) if (wdata->state.devtype == WIIMOTE_DEV_PENDING) wiimote_init_detect(wdata); + if (!wiimote_init_check(wdata)) + wiimote_init_hotplug(wdata); +} + +void __wiimote_schedule(struct wiimote_data *wdata) +{ + if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING)) + schedule_work(&wdata->init_worker); +} + +static void wiimote_schedule(struct wiimote_data *wdata) +{ + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + __wiimote_schedule(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static void wiimote_init_timeout(unsigned long arg) +{ + struct wiimote_data *wdata = (void*)arg; + + wiimote_schedule(wdata); } /* protocol handlers */ @@ -664,6 +1162,12 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) const __u8 *iter, *mods; const struct wiimod_ops *ops; + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_keys) { + ops->in_keys(wdata, payload); + return; + } + mods = wiimote_devtype_mods[wdata->state.devtype]; for (iter = mods; *iter != WIIMOD_NULL; ++iter) { ops = wiimod_table[*iter]; @@ -679,6 +1183,12 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) const __u8 *iter, *mods; const struct wiimod_ops *ops; + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_accel) { + ops->in_accel(wdata, payload); + return; + } + mods = wiimote_devtype_mods[wdata->state.devtype]; for (iter = mods; *iter != WIIMOD_NULL; ++iter) { ops = wiimod_table[*iter]; @@ -689,6 +1199,93 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) } } +static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len) +{ + if (!ops->in_ext) + return false; + if ((ops->flags & WIIMOD_FLAG_EXT8) && len < 8) + return false; + if ((ops->flags & WIIMOD_FLAG_EXT16) && len < 16) + return false; + + return true; +} + +static void handler_ext(struct wiimote_data *wdata, const __u8 *payload, + size_t len) +{ + const __u8 *iter, *mods; + const struct wiimod_ops *ops; + bool is_mp; + + if (len < 6) + return; + + /* if MP is active, track MP slot hotplugging */ + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + /* this bit is set for invalid events (eg. during hotplug) */ + if (payload[5] & 0x01) + return; + + if (payload[4] & 0x01) { + if (!(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED)) { + hid_dbg(wdata->hdev, "MP hotplug: 1\n"); + wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED; + __wiimote_schedule(wdata); + } + } else { + if (wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED) { + hid_dbg(wdata->hdev, "MP hotplug: 0\n"); + wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE; + __wiimote_schedule(wdata); + } + } + + /* detect MP data that is sent interleaved with EXT data */ + is_mp = payload[5] & 0x02; + } else { + is_mp = false; + } + + /* ignore EXT events if no extension is active */ + if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE) && !is_mp) + return; + + /* try forwarding to extension handler, first */ + ops = wiimod_ext_table[wdata->state.exttype]; + if (is_mp && ops->in_mp) { + ops->in_mp(wdata, payload); + return; + } else if (!is_mp && valid_ext_handler(ops, len)) { + ops->in_ext(wdata, payload); + return; + } + + /* try forwarding to MP handler */ + ops = &wiimod_mp; + if (is_mp && ops->in_mp) { + ops->in_mp(wdata, payload); + return; + } else if (!is_mp && valid_ext_handler(ops, len)) { + ops->in_ext(wdata, payload); + return; + } + + /* try forwarding to loaded modules */ + mods = wiimote_devtype_mods[wdata->state.devtype]; + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + ops = wiimod_table[*iter]; + if (is_mp && ops->in_mp) { + ops->in_mp(wdata, payload); + return; + } else if (!is_mp && valid_ext_handler(ops, len)) { + ops->in_ext(wdata, payload); + return; + } + } +} + #define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0) #define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1) #define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2) @@ -700,6 +1297,12 @@ static void handler_ir(struct wiimote_data *wdata, const __u8 *payload, const __u8 *iter, *mods; const struct wiimod_ops *ops; + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_ir) { + ops->in_ir(wdata, payload, packed, id); + return; + } + mods = wiimote_devtype_mods[wdata->state.devtype]; for (iter = mods; *iter != WIIMOD_NULL; ++iter) { ops = wiimod_table[*iter]; @@ -727,11 +1330,20 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload) /* update extension status */ if (payload[2] & 0x02) { - wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED; - wiiext_event(wdata, true); + if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED)) { + hid_dbg(wdata->hdev, "EXT hotplug: 1\n"); + wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED; + __wiimote_schedule(wdata); + } } else { - wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; - wiiext_event(wdata, false); + if (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED) { + hid_dbg(wdata->hdev, "EXT hotplug: 0\n"); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE; + __wiimote_schedule(wdata); + } } wdata->state.cmd_battery = payload[5]; @@ -791,7 +1403,7 @@ static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload) static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); - wiiext_handle(wdata, &payload[2]); + handler_ext(wdata, &payload[2], 8); } static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) @@ -807,7 +1419,7 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); - wiiext_handle(wdata, &payload[2]); + handler_ext(wdata, &payload[2], 19); } static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) @@ -817,14 +1429,14 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) ir_to_input1(wdata, &payload[4], true); ir_to_input2(wdata, &payload[7], false); ir_to_input3(wdata, &payload[9], true); - wiiext_handle(wdata, &payload[12]); + handler_ext(wdata, &payload[12], 9); } static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); handler_accel(wdata, payload); - wiiext_handle(wdata, &payload[5]); + handler_ext(wdata, &payload[5], 16); } static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) @@ -835,12 +1447,12 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) ir_to_input1(wdata, &payload[7], true); ir_to_input2(wdata, &payload[10], false); ir_to_input3(wdata, &payload[12], true); - wiiext_handle(wdata, &payload[15]); + handler_ext(wdata, &payload[15], 6); } static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload) { - wiiext_handle(wdata, payload); + handler_ext(wdata, payload, 21); } static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) @@ -960,16 +1572,27 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev) wdata->state.cmd_battery = 0xff; INIT_WORK(&wdata->init_worker, wiimote_init_worker); + setup_timer(&wdata->timer, wiimote_init_timeout, (long)wdata); return wdata; } static void wiimote_destroy(struct wiimote_data *wdata) { + unsigned long flags; + wiidebug_deinit(wdata); - wiiext_deinit(wdata); + + /* prevent init_worker from being scheduled again */ + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXITING; + spin_unlock_irqrestore(&wdata->state.lock, flags); cancel_work_sync(&wdata->init_worker); + del_timer_sync(&wdata->timer); + + wiimote_mp_unload(wdata); + wiimote_ext_unload(wdata); wiimote_modules_unload(wdata); cancel_work_sync(&wdata->queue.worker); hid_hw_close(wdata->hdev); @@ -1010,10 +1633,6 @@ static int wiimote_hid_probe(struct hid_device *hdev, goto err_stop; } - ret = wiiext_init(wdata); - if (ret) - goto err_free; - ret = wiidebug_init(wdata); if (ret) goto err_free; @@ -1021,7 +1640,7 @@ static int wiimote_hid_probe(struct hid_device *hdev, hid_info(hdev, "New device registered\n"); /* schedule device detection */ - schedule_work(&wdata->init_worker); + wiimote_schedule(wdata); return 0; diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 5cc593a..d3eef77 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -788,8 +788,19 @@ static const struct wiimod_ops wiimod_ir = { .in_ir = wiimod_ir_in_ir, }; +/* + * Motion Plus + */ + +const struct wiimod_ops wiimod_mp = { + .flags = 0, + .arg = 0, +}; + /* module table */ +static const struct wiimod_ops wiimod_dummy; + const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = { [WIIMOD_KEYS] = &wiimod_keys, [WIIMOD_RUMBLE] = &wiimod_rumble, @@ -801,3 +812,8 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = { [WIIMOD_ACCEL] = &wiimod_accel, [WIIMOD_IR] = &wiimod_ir, }; + +const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = { + [WIIMOTE_EXT_NONE] = &wiimod_dummy, + [WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy, +}; diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 3a2d3a1..0afc9f9 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -22,6 +22,7 @@ #include <linux/mutex.h> #include <linux/power_supply.h> #include <linux/spinlock.h> +#include <linux/timer.h> #define WIIMOTE_NAME "Nintendo Wii Remote" #define WIIMOTE_BUFSIZE 32 @@ -36,6 +37,12 @@ #define WIIPROTO_FLAG_IR_EXT 0x80 #define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ #define WIIPROTO_FLAG_EXT_PLUGGED 0x0100 +#define WIIPROTO_FLAG_EXT_USED 0x0200 +#define WIIPROTO_FLAG_EXT_ACTIVE 0x0400 +#define WIIPROTO_FLAG_MP_PLUGGED 0x0800 +#define WIIPROTO_FLAG_MP_USED 0x1000 +#define WIIPROTO_FLAG_MP_ACTIVE 0x2000 +#define WIIPROTO_FLAG_EXITING 0x4000 #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) @@ -75,6 +82,14 @@ enum wiimote_exttype { WIIMOTE_EXT_NUM, }; +enum wiimote_mptype { + WIIMOTE_MP_NONE, + WIIMOTE_MP_UNKNOWN, + WIIMOTE_MP_SINGLE, + WIIMOTE_MP_PASSTHROUGH_NUNCHUK, + WIIMOTE_MP_PASSTHROUGH_CLASSIC, +}; + struct wiimote_buf { __u8 data[HID_MAX_BUFFER_SIZE]; size_t size; @@ -94,6 +109,8 @@ struct wiimote_state { __u8 accel_split[2]; __u8 drm; __u8 devtype; + __u8 exttype; + __u8 mp; /* synchronous cmd requests */ struct mutex sync; @@ -115,6 +132,7 @@ struct wiimote_data { struct input_dev *accel; struct input_dev *ir; struct power_supply battery; + struct timer_list timer; struct wiimote_ext *ext; struct wiimote_debug *debug; @@ -140,6 +158,8 @@ enum wiimod_module { }; #define WIIMOD_FLAG_INPUT 0x0001 +#define WIIMOD_FLAG_EXT8 0x0002 +#define WIIMOD_FLAG_EXT16 0x0004 struct wiimod_ops { __u16 flags; @@ -153,9 +173,13 @@ struct wiimod_ops { void (*in_accel) (struct wiimote_data *wdata, const __u8 *accel); void (*in_ir) (struct wiimote_data *wdata, const __u8 *ir, bool packed, unsigned int id); + void (*in_mp) (struct wiimote_data *wdata, const __u8 *mp); + void (*in_ext) (struct wiimote_data *wdata, const __u8 *ext); }; extern const struct wiimod_ops *wiimod_table[WIIMOD_NUM]; +extern const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM]; +extern const struct wiimod_ops wiimod_mp; /* wiimote requests */ @@ -172,23 +196,48 @@ enum wiiproto_reqs { WIIPROTO_REQ_STATUS = 0x20, WIIPROTO_REQ_DATA = 0x21, WIIPROTO_REQ_RETURN = 0x22, + + /* DRM_K: BB*2 */ WIIPROTO_REQ_DRM_K = 0x30, + + /* DRM_KA: BB*2 AA*3 */ WIIPROTO_REQ_DRM_KA = 0x31, + + /* DRM_KE: BB*2 EE*8 */ WIIPROTO_REQ_DRM_KE = 0x32, + + /* DRM_KAI: BB*2 AA*3 II*12 */ WIIPROTO_REQ_DRM_KAI = 0x33, + + /* DRM_KEE: BB*2 EE*19 */ WIIPROTO_REQ_DRM_KEE = 0x34, + + /* DRM_KAE: BB*2 AA*3 EE*16 */ WIIPROTO_REQ_DRM_KAE = 0x35, + + /* DRM_KIE: BB*2 II*10 EE*9 */ WIIPROTO_REQ_DRM_KIE = 0x36, + + /* DRM_KAIE: BB*2 AA*3 II*10 EE*6 */ WIIPROTO_REQ_DRM_KAIE = 0x37, + + /* DRM_E: EE*21 */ WIIPROTO_REQ_DRM_E = 0x3d, + + /* DRM_SKAI1: BB*2 AA*1 II*18 */ WIIPROTO_REQ_DRM_SKAI1 = 0x3e, + + /* DRM_SKAI2: BB*2 AA*1 II*18 */ WIIPROTO_REQ_DRM_SKAI2 = 0x3f, + WIIPROTO_REQ_MAX }; #define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \ dev)) +void __wiimote_schedule(struct wiimote_data *wdata); + extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm); extern void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble); extern void wiiproto_req_leds(struct wiimote_data *wdata, int leds); |