diff options
Diffstat (limited to 'drivers/hid/hid-wiimote-core.c')
-rw-r--r-- | drivers/hid/hid-wiimote-core.c | 153 |
1 files changed, 152 insertions, 1 deletions
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 02656a8cc7d7..76d2c73eb696 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -399,6 +399,45 @@ ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem, return ret; } +/* requires the cmd-mutex to be held */ +static int wiimote_cmd_init_ext(struct wiimote_data *wdata) +{ + __u8 wmem; + int ret; + + /* initialize extension */ + wmem = 0x55; + ret = wiimote_cmd_write(wdata, 0xa400f0, &wmem, sizeof(wmem)); + if (ret) + return ret; + + /* disable default encryption */ + wmem = 0x0; + ret = wiimote_cmd_write(wdata, 0xa400fb, &wmem, sizeof(wmem)); + if (ret) + return ret; + + return 0; +} + +/* requires the cmd-mutex to be held */ +static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata) +{ + __u8 rmem[6]; + int ret; + + /* read extension ID */ + ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6); + if (ret != 6) + return WIIMOTE_EXT_NONE; + + if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && + rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) + return WIIMOTE_EXT_NONE; + + return WIIMOTE_EXT_UNKNOWN; +} + static int wiimote_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -662,6 +701,105 @@ static void wiimote_ir_close(struct input_dev *dev) wiimote_init_ir(wdata, 0); } +/* device (re-)initialization and detection */ + +static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = { + [WIIMOTE_DEV_PENDING] = "Pending", + [WIIMOTE_DEV_UNKNOWN] = "Unknown", + [WIIMOTE_DEV_GENERIC] = "Generic", + [WIIMOTE_DEV_GEN10] = "Nintendo Wii Remote (Gen 1)", + [WIIMOTE_DEV_GEN20] = "Nintendo Wii Remote Plus (Gen 2)", +}; + +/* Try to guess the device type based on all collected information. We + * first try to detect by static extension types, then VID/PID and the + * device name. If we cannot detect the device, we use + * WIIMOTE_DEV_GENERIC so all modules will get probed on the device. */ +static void wiimote_init_set_type(struct wiimote_data *wdata, + __u8 exttype) +{ + __u8 devtype = WIIMOTE_DEV_GENERIC; + __u16 vendor, product; + const char *name; + + vendor = wdata->hdev->vendor; + product = wdata->hdev->product; + name = wdata->hdev->name; + + if (!strcmp(name, "Nintendo RVL-CNT-01")) { + devtype = WIIMOTE_DEV_GEN10; + goto done; + } else if (!strcmp(name, "Nintendo RVL-CNT-01-TR")) { + devtype = WIIMOTE_DEV_GEN20; + goto done; + } + + if (vendor == USB_VENDOR_ID_NINTENDO) { + if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) { + devtype = WIIMOTE_DEV_GEN10; + goto done; + } else if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE2) { + devtype = WIIMOTE_DEV_GEN20; + goto done; + } + } + +done: + if (devtype == WIIMOTE_DEV_GENERIC) + hid_info(wdata->hdev, "cannot detect device; NAME: %s VID: %04x PID: %04x EXT: %04x\n", + name, vendor, product, exttype); + else + hid_info(wdata->hdev, "detected device: %s\n", + wiimote_devtype_names[devtype]); + + spin_lock_irq(&wdata->state.lock); + wdata->state.devtype = devtype; + spin_unlock_irq(&wdata->state.lock); +} + +static void wiimote_init_detect(struct wiimote_data *wdata) +{ + __u8 exttype = WIIMOTE_EXT_NONE; + bool ext; + int ret; + + wiimote_cmd_acquire_noint(wdata); + + spin_lock_irq(&wdata->state.lock); + wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); + wiiproto_req_status(wdata); + spin_unlock_irq(&wdata->state.lock); + + ret = wiimote_cmd_wait_noint(wdata); + if (ret) + goto out_release; + + spin_lock_irq(&wdata->state.lock); + ext = wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED; + spin_unlock_irq(&wdata->state.lock); + + if (!ext) + goto out_release; + + wiimote_cmd_init_ext(wdata); + exttype = wiimote_cmd_read_ext(wdata); + +out_release: + wiimote_cmd_release(wdata); + wiimote_init_set_type(wdata, exttype); +} + +static void wiimote_init_worker(struct work_struct *work) +{ + struct wiimote_data *wdata = container_of(work, struct wiimote_data, + init_worker); + + if (wdata->state.devtype == WIIMOTE_DEV_PENDING) + wiimote_init_detect(wdata); +} + +/* protocol handlers */ + static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) { input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT], @@ -776,7 +914,14 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload) { handler_status_K(wdata, payload); - wiiext_event(wdata, payload[2] & 0x02); + /* update extension status */ + if (payload[2] & 0x02) { + wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED; + wiiext_event(wdata, true); + } else { + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wiiext_event(wdata, false); + } if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) { wdata->state.cmd_battery = payload[5]; @@ -1135,6 +1280,8 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev) mutex_init(&wdata->state.sync); wdata->state.drm = WIIPROTO_REQ_DRM_K; + INIT_WORK(&wdata->init_worker, wiimote_init_worker); + return wdata; err_ir: @@ -1157,6 +1304,7 @@ static void wiimote_destroy(struct wiimote_data *wdata) input_unregister_device(wdata->accel); input_unregister_device(wdata->ir); input_unregister_device(wdata->input); + cancel_work_sync(&wdata->init_worker); cancel_work_sync(&wdata->queue.worker); hid_hw_close(wdata->hdev); hid_hw_stop(wdata->hdev); @@ -1253,6 +1401,9 @@ static int wiimote_hid_probe(struct hid_device *hdev, wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1); spin_unlock_irq(&wdata->state.lock); + /* schedule device detection */ + schedule_work(&wdata->init_worker); + return 0; err_free: |