Hi Jiri On Sun, May 5, 2013 at 11:12 PM, David Herrmann <dh.herrmann@xxxxxxxxx> wrote: > The Wii Remote has several extension ports. The first port (EXT) provides > hotplug events whenever an extension is plugged. The second port (MP) > does not provide hotplug events by default. Instead, we have to map MP > into EXT to get events for it. > > This patch introduces hotplug support for extensions. It is fairly > complicated to get this right because the Wii Remote sends a lot of > noise-hotplug events while activating extension ports. We need to filter > the events and only handle the events that are real hotplug events. > > Mapping MP into EXT is easy. But if we want both, MP _and_ EXT at the same > time, we need to map MP into EXT and enable a passthrough-mode. This will > then send real EXT events through the mapped MP interleaved with real MP > events. But once MP is mapped, we no longer have access to the real EXT > registers so we need to perform setup _before_ mapping MP. Furthermore, we > no longer can read EXT IDs so we cannot verify if EXT is still the same > extension that we expect it to be. > We deal with this by unmapping MP whenever we got into a situation where > EXT might have changed. We then re-read EXT and MP and remap everything. > > The real Wii Console takes a fairly easy approach: It simply reconnects to > the device on hotplug events that it didn't expect. So if a program wants > MP events, but MP is disconnected, it fails and reconnects so it can wait > for MP hotplug events again. > This simplifies hotplugging a lot because we just react on PLUG events and > ignore UNPLUG events. > The more sophisticated Wii applications avoid reconnection (well, they > still reconnect during many weird events, but at least not during UNPLUG) > but they start polling the device. This allows them to disable the device, > poll for the extension ports to settle and then initialize them again. > Unfortunately, this approach fails whenever an extension is replugged > while it is initialized. We would loose UNPLUG events and polling the > device later will give unreliable results because the extension port might > be in some weird state, even though it's actually unplugged. > > Our approach is a real HOTPLUG approch. We keep track of the EXT and > mapped MP hotplug events whenever they occur. We then re-evaluate the > device state and initialize any possible new extension or deinitialize any > gone extension. Only during initialization, we set an extension port > ACTIVE. However, during an unplug event we mark them as INACTIVE. This > guarantess that a fast UNPLUG -> PLUG event sequence doesn't keep them > marked as PLUGGED+ACTIVE but only PLUGGED. > To deal with annoying noise-hotplug events during extension mapping, we > simply rescan the device before performing any mapping. This allows us to > ignore all the noise events as long as the device is in the correct state. > > Long story short: EXT and MP registers are sparsely known and we need to > jump through hoops to get reliable HOTPLUG working. But while Nintendo > needs *FOUR* Bluetooth reconnections for the shortest imaginable > boot->menu->game->menu->shutdown sequence, we now need *ZERO*. > > As always, 3rd party devices tend to break whenever we behave differently > than the original Wii. So there are also devices which _expect_ a > disconnect after UNPLUG. Obviously, these devices won't benefit from this > patch. But all official devices were tested extensively and work great > during any hotplug sequence. Yay! > > Signed-off-by: David Herrmann <dh.herrmann@xxxxxxxxx> > --- > drivers/hid/hid-wiimote-core.c | 669 ++++++++++++++++++++++++++++++++++++-- > drivers/hid/hid-wiimote-modules.c | 16 + > 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); We need an additional "wiimote_init_hotplug(wdata);" in this if-clause for devices with built-in MP to detect it right during init. Otherwise, MP is not initialized until you plug some other extension. That's because the MP-timer is disabled for devices with built-in MP. Other than that I didn't get any bug-reports. And I haven't found any bugs, either. I want to avoid resending the whole series, so I will send a fixup patch on top of your branch once you applied it (if that is ok?). Cheers David > + 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); > -- > 1.8.2.2 > -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html