HID: wiimote: add pro-controller analog stick calibration
The analog sticks of the pro-controller might report slightly off values. To guarantee a uniform setup, we now calibrate analog-stick values during pro-controller setup. Unfortunately, the pro-controller fails during normal EEPROM reads and I couldn't figure out whether there are any calibration values stored on the device. Therefore, we now use the first values reported by the device (iff they are not _way_ off, which would indicate movement) to initialize the calibration values. To allow users to change this calibration data, we provide a pro_calib sysfs attribute. We also change the "flat" values so user-space correctly smoothes our data. It makes slightly off zero-positions less visible while still guaranteeing highly precise movement reports. Note that the pro controller reports zero-positions in a quite huge range (at least: -100 to +100). Reported-by: Rafael Brune <mail@rbrune.de> Tested-by: Rafael Brune <mail@rbrune.de> Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
0abda6fa81
commit
08072dde33
|
@ -57,3 +57,21 @@ Description: This attribute is only provided if the device was detected as a
|
||||||
Calibration data is already applied by the kernel to all input
|
Calibration data is already applied by the kernel to all input
|
||||||
values but may be used by user-space to perform other
|
values but may be used by user-space to perform other
|
||||||
transformations.
|
transformations.
|
||||||
|
|
||||||
|
What: /sys/bus/hid/drivers/wiimote/<dev>/pro_calib
|
||||||
|
Date: October 2013
|
||||||
|
KernelVersion: 3.13
|
||||||
|
Contact: David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
Description: This attribute is only provided if the device was detected as a
|
||||||
|
pro-controller. It provides a single line with 4 calibration
|
||||||
|
values for all 4 analog sticks. Format is: "x1:y1 x2:y2". Data
|
||||||
|
is prefixed with a +/-. Each value is a signed 16bit number.
|
||||||
|
Data is encoded as decimal numbers and specifies the offsets of
|
||||||
|
the analog sticks of the pro-controller.
|
||||||
|
Calibration data is already applied by the kernel to all input
|
||||||
|
values but may be used by user-space to perform other
|
||||||
|
transformations.
|
||||||
|
Calibration data is detected by the kernel during device setup.
|
||||||
|
You can write "scan\n" into this file to re-trigger calibration.
|
||||||
|
You can also write data directly in the form "x1:y1 x2:y2" to
|
||||||
|
set the calibration values manually.
|
||||||
|
|
|
@ -1655,10 +1655,39 @@ static void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext)
|
||||||
ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8);
|
ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8);
|
||||||
ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8);
|
ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8);
|
||||||
|
|
||||||
input_report_abs(wdata->extension.input, ABS_X, lx - 0x800);
|
/* zero-point offsets */
|
||||||
input_report_abs(wdata->extension.input, ABS_Y, 0x800 - ly);
|
lx -= 0x800;
|
||||||
input_report_abs(wdata->extension.input, ABS_RX, rx - 0x800);
|
ly = 0x800 - ly;
|
||||||
input_report_abs(wdata->extension.input, ABS_RY, 0x800 - ry);
|
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,
|
input_report_key(wdata->extension.input,
|
||||||
wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT],
|
wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT],
|
||||||
|
@ -1766,12 +1795,70 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,
|
||||||
return 0;
|
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,
|
static int wiimod_pro_probe(const struct wiimod_ops *ops,
|
||||||
struct wiimote_data *wdata)
|
struct wiimote_data *wdata)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
|
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();
|
wdata->extension.input = input_allocate_device();
|
||||||
if (!wdata->extension.input)
|
if (!wdata->extension.input)
|
||||||
|
@ -1786,6 +1873,13 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
|
||||||
goto err_free;
|
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->open = wiimod_pro_open;
|
||||||
wdata->extension.input->close = wiimod_pro_close;
|
wdata->extension.input->close = wiimod_pro_close;
|
||||||
wdata->extension.input->dev.parent = &wdata->hdev->dev;
|
wdata->extension.input->dev.parent = &wdata->hdev->dev;
|
||||||
|
@ -1806,20 +1900,23 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
|
||||||
set_bit(ABS_RX, wdata->extension.input->absbit);
|
set_bit(ABS_RX, wdata->extension.input->absbit);
|
||||||
set_bit(ABS_RY, wdata->extension.input->absbit);
|
set_bit(ABS_RY, wdata->extension.input->absbit);
|
||||||
input_set_abs_params(wdata->extension.input,
|
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,
|
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,
|
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,
|
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);
|
ret = input_register_device(wdata->extension.input);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free;
|
goto err_file;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_file:
|
||||||
|
device_remove_file(&wdata->hdev->dev,
|
||||||
|
&dev_attr_pro_calib);
|
||||||
err_free:
|
err_free:
|
||||||
input_free_device(wdata->extension.input);
|
input_free_device(wdata->extension.input);
|
||||||
wdata->extension.input = NULL;
|
wdata->extension.input = NULL;
|
||||||
|
@ -1837,6 +1934,8 @@ static void wiimod_pro_remove(const struct wiimod_ops *ops,
|
||||||
input_unregister_device(wdata->extension.input);
|
input_unregister_device(wdata->extension.input);
|
||||||
wdata->extension.input = NULL;
|
wdata->extension.input = NULL;
|
||||||
cancel_work_sync(&wdata->rumble_worker);
|
cancel_work_sync(&wdata->rumble_worker);
|
||||||
|
device_remove_file(&wdata->hdev->dev,
|
||||||
|
&dev_attr_pro_calib);
|
||||||
|
|
||||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||||
wiiproto_req_rumble(wdata, 0);
|
wiiproto_req_rumble(wdata, 0);
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#define WIIPROTO_FLAG_DRM_LOCKED 0x8000
|
#define WIIPROTO_FLAG_DRM_LOCKED 0x8000
|
||||||
#define WIIPROTO_FLAG_BUILTIN_MP 0x010000
|
#define WIIPROTO_FLAG_BUILTIN_MP 0x010000
|
||||||
#define WIIPROTO_FLAG_NO_MP 0x020000
|
#define WIIPROTO_FLAG_NO_MP 0x020000
|
||||||
|
#define WIIPROTO_FLAG_PRO_CALIB_DONE 0x040000
|
||||||
|
|
||||||
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
|
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
|
||||||
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
|
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
|
||||||
|
@ -135,6 +136,7 @@ struct wiimote_state {
|
||||||
|
|
||||||
/* calibration/cache data */
|
/* calibration/cache data */
|
||||||
__u16 calib_bboard[4][3];
|
__u16 calib_bboard[4][3];
|
||||||
|
__s16 calib_pro_sticks[4];
|
||||||
__u8 cache_rumble;
|
__u8 cache_rumble;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue