diff options
Diffstat (limited to 'drivers/hid/hid-wiimote-modules.c')
-rw-r--r-- | drivers/hid/hid-wiimote-modules.c | 157 |
1 files changed, 137 insertions, 20 deletions
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 2e7d644dba18..6b61f01e01e7 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -119,12 +119,22 @@ static const struct wiimod_ops wiimod_keys = { * the rumble motor, this flag shouldn't be set. */ +/* used by wiimod_rumble and wiipro_rumble */ +static void wiimod_rumble_worker(struct work_struct *work) +{ + struct wiimote_data *wdata = container_of(work, struct wiimote_data, + rumble_worker); + + spin_lock_irq(&wdata->state.lock); + wiiproto_req_rumble(wdata, wdata->state.cache_rumble); + spin_unlock_irq(&wdata->state.lock); +} + static int wiimod_rumble_play(struct input_dev *dev, void *data, struct ff_effect *eff) { struct wiimote_data *wdata = input_get_drvdata(dev); __u8 value; - unsigned long flags; /* * The wiimote supports only a single rumble motor so if any magnitude @@ -137,9 +147,10 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data, else value = 0; - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_rumble(wdata, value); - spin_unlock_irqrestore(&wdata->state.lock, flags); + /* Locking state.lock here might deadlock with input_event() calls. + * schedule_work acts as barrier. Merging multiple changes is fine. */ + wdata->state.cache_rumble = value; + schedule_work(&wdata->rumble_worker); return 0; } @@ -147,6 +158,8 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data, static int wiimod_rumble_probe(const struct wiimod_ops *ops, struct wiimote_data *wdata) { + INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker); + set_bit(FF_RUMBLE, wdata->input->ffbit); if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play)) return -ENOMEM; @@ -159,6 +172,8 @@ static void wiimod_rumble_remove(const struct wiimod_ops *ops, { unsigned long flags; + cancel_work_sync(&wdata->rumble_worker); + spin_lock_irqsave(&wdata->state.lock, flags); wiiproto_req_rumble(wdata, 0); spin_unlock_irqrestore(&wdata->state.lock, flags); @@ -1640,10 +1655,39 @@ static void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext) ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8); ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8); - input_report_abs(wdata->extension.input, ABS_X, lx - 0x800); - input_report_abs(wdata->extension.input, ABS_Y, ly - 0x800); - input_report_abs(wdata->extension.input, ABS_RX, rx - 0x800); - input_report_abs(wdata->extension.input, ABS_RY, ry - 0x800); + /* zero-point offsets */ + lx -= 0x800; + ly = 0x800 - ly; + rx -= 0x800; + ry = 0x800 - ry; + + /* Trivial automatic calibration. We don't know any calibration data + * in the EEPROM so we must use the first report to calibrate the + * null-position of the analog sticks. Users can retrigger calibration + * via sysfs, or set it explicitly. If data is off more than abs(500), + * we skip calibration as the sticks are likely to be moved already. */ + if (!(wdata->state.flags & WIIPROTO_FLAG_PRO_CALIB_DONE)) { + wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE; + if (abs(lx) < 500) + wdata->state.calib_pro_sticks[0] = -lx; + if (abs(ly) < 500) + wdata->state.calib_pro_sticks[1] = -ly; + if (abs(rx) < 500) + wdata->state.calib_pro_sticks[2] = -rx; + if (abs(ry) < 500) + wdata->state.calib_pro_sticks[3] = -ry; + } + + /* apply calibration data */ + lx += wdata->state.calib_pro_sticks[0]; + ly += wdata->state.calib_pro_sticks[1]; + rx += wdata->state.calib_pro_sticks[2]; + ry += wdata->state.calib_pro_sticks[3]; + + input_report_abs(wdata->extension.input, ABS_X, lx); + input_report_abs(wdata->extension.input, ABS_Y, ly); + input_report_abs(wdata->extension.input, ABS_RX, rx); + input_report_abs(wdata->extension.input, ABS_RY, ry); input_report_key(wdata->extension.input, wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT], @@ -1731,7 +1775,6 @@ static int wiimod_pro_play(struct input_dev *dev, void *data, { struct wiimote_data *wdata = input_get_drvdata(dev); __u8 value; - unsigned long flags; /* * The wiimote supports only a single rumble motor so if any magnitude @@ -1744,17 +1787,78 @@ static int wiimod_pro_play(struct input_dev *dev, void *data, else value = 0; - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_rumble(wdata, value); - spin_unlock_irqrestore(&wdata->state.lock, flags); + /* Locking state.lock here might deadlock with input_event() calls. + * schedule_work acts as barrier. Merging multiple changes is fine. */ + wdata->state.cache_rumble = value; + schedule_work(&wdata->rumble_worker); return 0; } +static ssize_t wiimod_pro_calib_show(struct device *dev, + struct device_attribute *attr, + char *out) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + int r; + + r = 0; + r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[0]); + r += sprintf(&out[r], "%+06hd ", wdata->state.calib_pro_sticks[1]); + r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[2]); + r += sprintf(&out[r], "%+06hd\n", wdata->state.calib_pro_sticks[3]); + + return r; +} + +static ssize_t wiimod_pro_calib_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + int r; + s16 x1, y1, x2, y2; + + if (!strncmp(buf, "scan\n", 5)) { + spin_lock_irq(&wdata->state.lock); + wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE; + spin_unlock_irq(&wdata->state.lock); + } else { + r = sscanf(buf, "%hd:%hd %hd:%hd", &x1, &y1, &x2, &y2); + if (r != 4) + return -EINVAL; + + spin_lock_irq(&wdata->state.lock); + wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE; + spin_unlock_irq(&wdata->state.lock); + + wdata->state.calib_pro_sticks[0] = x1; + wdata->state.calib_pro_sticks[1] = y1; + wdata->state.calib_pro_sticks[2] = x2; + wdata->state.calib_pro_sticks[3] = y2; + } + + return strnlen(buf, PAGE_SIZE); +} + +static DEVICE_ATTR(pro_calib, S_IRUGO|S_IWUSR|S_IWGRP, wiimod_pro_calib_show, + wiimod_pro_calib_store); + static int wiimod_pro_probe(const struct wiimod_ops *ops, struct wiimote_data *wdata) { int ret, i; + unsigned long flags; + + INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker); + wdata->state.calib_pro_sticks[0] = 0; + wdata->state.calib_pro_sticks[1] = 0; + wdata->state.calib_pro_sticks[2] = 0; + wdata->state.calib_pro_sticks[3] = 0; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE; + spin_unlock_irqrestore(&wdata->state.lock, flags); wdata->extension.input = input_allocate_device(); if (!wdata->extension.input) @@ -1769,6 +1873,13 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops, goto err_free; } + ret = device_create_file(&wdata->hdev->dev, + &dev_attr_pro_calib); + if (ret) { + hid_err(wdata->hdev, "cannot create sysfs attribute\n"); + goto err_free; + } + wdata->extension.input->open = wiimod_pro_open; wdata->extension.input->close = wiimod_pro_close; wdata->extension.input->dev.parent = &wdata->hdev->dev; @@ -1789,20 +1900,23 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops, set_bit(ABS_RX, wdata->extension.input->absbit); set_bit(ABS_RY, wdata->extension.input->absbit); input_set_abs_params(wdata->extension.input, - ABS_X, -0x800, 0x800, 2, 4); + ABS_X, -0x400, 0x400, 4, 100); input_set_abs_params(wdata->extension.input, - ABS_Y, -0x800, 0x800, 2, 4); + ABS_Y, -0x400, 0x400, 4, 100); input_set_abs_params(wdata->extension.input, - ABS_RX, -0x800, 0x800, 2, 4); + ABS_RX, -0x400, 0x400, 4, 100); input_set_abs_params(wdata->extension.input, - ABS_RY, -0x800, 0x800, 2, 4); + ABS_RY, -0x400, 0x400, 4, 100); ret = input_register_device(wdata->extension.input); if (ret) - goto err_free; + goto err_file; return 0; +err_file: + device_remove_file(&wdata->hdev->dev, + &dev_attr_pro_calib); err_free: input_free_device(wdata->extension.input); wdata->extension.input = NULL; @@ -1817,12 +1931,15 @@ static void wiimod_pro_remove(const struct wiimod_ops *ops, if (!wdata->extension.input) return; + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; + cancel_work_sync(&wdata->rumble_worker); + device_remove_file(&wdata->hdev->dev, + &dev_attr_pro_calib); + spin_lock_irqsave(&wdata->state.lock, flags); wiiproto_req_rumble(wdata, 0); spin_unlock_irqrestore(&wdata->state.lock, flags); - - input_unregister_device(wdata->extension.input); - wdata->extension.input = NULL; } static const struct wiimod_ops wiimod_pro = { |