18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Device Modules for Nintendo Wii / Wii U HID Driver
48c2ecf20Sopenharmony_ci * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * Wiimote Modules
128c2ecf20Sopenharmony_ci * Nintendo devices provide different peripherals and many new devices lack
138c2ecf20Sopenharmony_ci * initial features like the IR camera. Therefore, each peripheral device is
148c2ecf20Sopenharmony_ci * implemented as an independent module and we probe on each device only the
158c2ecf20Sopenharmony_ci * modules for the hardware that really is available.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Module registration is sequential. Unregistration is done in reverse order.
188c2ecf20Sopenharmony_ci * After device detection, the needed modules are loaded. Users can trigger
198c2ecf20Sopenharmony_ci * re-detection which causes all modules to be unloaded and then reload the
208c2ecf20Sopenharmony_ci * modules for the new detected device.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * wdata->input is a shared input device. It is always initialized prior to
238c2ecf20Sopenharmony_ci * module registration. If at least one registered module is marked as
248c2ecf20Sopenharmony_ci * WIIMOD_FLAG_INPUT, then the input device will get registered after all
258c2ecf20Sopenharmony_ci * modules were registered.
268c2ecf20Sopenharmony_ci * Please note that it is unregistered _before_ the "remove" callbacks are
278c2ecf20Sopenharmony_ci * called. This guarantees that no input interaction is done, anymore. However,
288c2ecf20Sopenharmony_ci * the wiimote core keeps a reference to the input device so it is freed only
298c2ecf20Sopenharmony_ci * after all modules were removed. It is safe to send events to unregistered
308c2ecf20Sopenharmony_ci * input devices.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/device.h>
348c2ecf20Sopenharmony_ci#include <linux/hid.h>
358c2ecf20Sopenharmony_ci#include <linux/input.h>
368c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
378c2ecf20Sopenharmony_ci#include "hid-wiimote.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Keys
418c2ecf20Sopenharmony_ci * The initial Wii Remote provided a bunch of buttons that are reported as
428c2ecf20Sopenharmony_ci * part of the core protocol. Many later devices dropped these and report
438c2ecf20Sopenharmony_ci * invalid data in the core button reports. Load this only on devices which
448c2ecf20Sopenharmony_ci * correctly send button reports.
458c2ecf20Sopenharmony_ci * It uses the shared input device.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic const __u16 wiimod_keys_map[] = {
498c2ecf20Sopenharmony_ci	KEY_LEFT,	/* WIIPROTO_KEY_LEFT */
508c2ecf20Sopenharmony_ci	KEY_RIGHT,	/* WIIPROTO_KEY_RIGHT */
518c2ecf20Sopenharmony_ci	KEY_UP,		/* WIIPROTO_KEY_UP */
528c2ecf20Sopenharmony_ci	KEY_DOWN,	/* WIIPROTO_KEY_DOWN */
538c2ecf20Sopenharmony_ci	KEY_NEXT,	/* WIIPROTO_KEY_PLUS */
548c2ecf20Sopenharmony_ci	KEY_PREVIOUS,	/* WIIPROTO_KEY_MINUS */
558c2ecf20Sopenharmony_ci	BTN_1,		/* WIIPROTO_KEY_ONE */
568c2ecf20Sopenharmony_ci	BTN_2,		/* WIIPROTO_KEY_TWO */
578c2ecf20Sopenharmony_ci	BTN_A,		/* WIIPROTO_KEY_A */
588c2ecf20Sopenharmony_ci	BTN_B,		/* WIIPROTO_KEY_B */
598c2ecf20Sopenharmony_ci	BTN_MODE,	/* WIIPROTO_KEY_HOME */
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void wiimod_keys_in_keys(struct wiimote_data *wdata, const __u8 *keys)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_LEFT],
658c2ecf20Sopenharmony_ci							!!(keys[0] & 0x01));
668c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_RIGHT],
678c2ecf20Sopenharmony_ci							!!(keys[0] & 0x02));
688c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_DOWN],
698c2ecf20Sopenharmony_ci							!!(keys[0] & 0x04));
708c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_UP],
718c2ecf20Sopenharmony_ci							!!(keys[0] & 0x08));
728c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_PLUS],
738c2ecf20Sopenharmony_ci							!!(keys[0] & 0x10));
748c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_TWO],
758c2ecf20Sopenharmony_ci							!!(keys[1] & 0x01));
768c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_ONE],
778c2ecf20Sopenharmony_ci							!!(keys[1] & 0x02));
788c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_B],
798c2ecf20Sopenharmony_ci							!!(keys[1] & 0x04));
808c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_A],
818c2ecf20Sopenharmony_ci							!!(keys[1] & 0x08));
828c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_MINUS],
838c2ecf20Sopenharmony_ci							!!(keys[1] & 0x10));
848c2ecf20Sopenharmony_ci	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_HOME],
858c2ecf20Sopenharmony_ci							!!(keys[1] & 0x80));
868c2ecf20Sopenharmony_ci	input_sync(wdata->input);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int wiimod_keys_probe(const struct wiimod_ops *ops,
908c2ecf20Sopenharmony_ci			     struct wiimote_data *wdata)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	unsigned int i;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->input->evbit);
958c2ecf20Sopenharmony_ci	for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
968c2ecf20Sopenharmony_ci		set_bit(wiimod_keys_map[i], wdata->input->keybit);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_keys = {
1028c2ecf20Sopenharmony_ci	.flags = WIIMOD_FLAG_INPUT,
1038c2ecf20Sopenharmony_ci	.arg = 0,
1048c2ecf20Sopenharmony_ci	.probe = wiimod_keys_probe,
1058c2ecf20Sopenharmony_ci	.remove = NULL,
1068c2ecf20Sopenharmony_ci	.in_keys = wiimod_keys_in_keys,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Rumble
1118c2ecf20Sopenharmony_ci * Nearly all devices provide a rumble feature. A small motor for
1128c2ecf20Sopenharmony_ci * force-feedback effects. We provide an FF_RUMBLE memless ff device on the
1138c2ecf20Sopenharmony_ci * shared input device if this module is loaded.
1148c2ecf20Sopenharmony_ci * The rumble motor is controlled via a flag on almost every output report so
1158c2ecf20Sopenharmony_ci * the wiimote core handles the rumble flag. But if a device doesn't provide
1168c2ecf20Sopenharmony_ci * the rumble motor, this flag shouldn't be set.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* used by wiimod_rumble and wiipro_rumble */
1208c2ecf20Sopenharmony_cistatic void wiimod_rumble_worker(struct work_struct *work)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = container_of(work, struct wiimote_data,
1238c2ecf20Sopenharmony_ci						  rumble_worker);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
1268c2ecf20Sopenharmony_ci	wiiproto_req_rumble(wdata, wdata->state.cache_rumble);
1278c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int wiimod_rumble_play(struct input_dev *dev, void *data,
1318c2ecf20Sopenharmony_ci			      struct ff_effect *eff)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
1348c2ecf20Sopenharmony_ci	__u8 value;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * The wiimote supports only a single rumble motor so if any magnitude
1388c2ecf20Sopenharmony_ci	 * is set to non-zero then we start the rumble motor. If both are set to
1398c2ecf20Sopenharmony_ci	 * zero, we stop the rumble motor.
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
1438c2ecf20Sopenharmony_ci		value = 1;
1448c2ecf20Sopenharmony_ci	else
1458c2ecf20Sopenharmony_ci		value = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* Locking state.lock here might deadlock with input_event() calls.
1488c2ecf20Sopenharmony_ci	 * schedule_work acts as barrier. Merging multiple changes is fine. */
1498c2ecf20Sopenharmony_ci	wdata->state.cache_rumble = value;
1508c2ecf20Sopenharmony_ci	schedule_work(&wdata->rumble_worker);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int wiimod_rumble_probe(const struct wiimod_ops *ops,
1568c2ecf20Sopenharmony_ci			       struct wiimote_data *wdata)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	set_bit(FF_RUMBLE, wdata->input->ffbit);
1618c2ecf20Sopenharmony_ci	if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play))
1628c2ecf20Sopenharmony_ci		return -ENOMEM;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void wiimod_rumble_remove(const struct wiimod_ops *ops,
1688c2ecf20Sopenharmony_ci				 struct wiimote_data *wdata)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	unsigned long flags;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	cancel_work_sync(&wdata->rumble_worker);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
1758c2ecf20Sopenharmony_ci	wiiproto_req_rumble(wdata, 0);
1768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_rumble = {
1808c2ecf20Sopenharmony_ci	.flags = WIIMOD_FLAG_INPUT,
1818c2ecf20Sopenharmony_ci	.arg = 0,
1828c2ecf20Sopenharmony_ci	.probe = wiimod_rumble_probe,
1838c2ecf20Sopenharmony_ci	.remove = wiimod_rumble_remove,
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/*
1878c2ecf20Sopenharmony_ci * Battery
1888c2ecf20Sopenharmony_ci * 1 byte of battery capacity information is sent along every protocol status
1898c2ecf20Sopenharmony_ci * report. The wiimote core caches it but we try to update it on every
1908c2ecf20Sopenharmony_ci * user-space request.
1918c2ecf20Sopenharmony_ci * This is supported by nearly every device so it's almost always enabled.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic enum power_supply_property wiimod_battery_props[] = {
1958c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
1968c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_SCOPE,
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int wiimod_battery_get_property(struct power_supply *psy,
2008c2ecf20Sopenharmony_ci				       enum power_supply_property psp,
2018c2ecf20Sopenharmony_ci				       union power_supply_propval *val)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = power_supply_get_drvdata(psy);
2048c2ecf20Sopenharmony_ci	int ret = 0, state;
2058c2ecf20Sopenharmony_ci	unsigned long flags;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (psp == POWER_SUPPLY_PROP_SCOPE) {
2088c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
2098c2ecf20Sopenharmony_ci		return 0;
2108c2ecf20Sopenharmony_ci	} else if (psp != POWER_SUPPLY_PROP_CAPACITY) {
2118c2ecf20Sopenharmony_ci		return -EINVAL;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = wiimote_cmd_acquire(wdata);
2158c2ecf20Sopenharmony_ci	if (ret)
2168c2ecf20Sopenharmony_ci		return ret;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
2198c2ecf20Sopenharmony_ci	wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
2208c2ecf20Sopenharmony_ci	wiiproto_req_status(wdata);
2218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	wiimote_cmd_wait(wdata);
2248c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
2278c2ecf20Sopenharmony_ci	state = wdata->state.cmd_battery;
2288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	val->intval = state * 100 / 255;
2318c2ecf20Sopenharmony_ci	return ret;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int wiimod_battery_probe(const struct wiimod_ops *ops,
2358c2ecf20Sopenharmony_ci				struct wiimote_data *wdata)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = { .drv_data = wdata, };
2388c2ecf20Sopenharmony_ci	int ret;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	wdata->battery_desc.properties = wiimod_battery_props;
2418c2ecf20Sopenharmony_ci	wdata->battery_desc.num_properties = ARRAY_SIZE(wiimod_battery_props);
2428c2ecf20Sopenharmony_ci	wdata->battery_desc.get_property = wiimod_battery_get_property;
2438c2ecf20Sopenharmony_ci	wdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
2448c2ecf20Sopenharmony_ci	wdata->battery_desc.use_for_apm = 0;
2458c2ecf20Sopenharmony_ci	wdata->battery_desc.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s",
2468c2ecf20Sopenharmony_ci					     wdata->hdev->uniq);
2478c2ecf20Sopenharmony_ci	if (!wdata->battery_desc.name)
2488c2ecf20Sopenharmony_ci		return -ENOMEM;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	wdata->battery = power_supply_register(&wdata->hdev->dev,
2518c2ecf20Sopenharmony_ci					       &wdata->battery_desc,
2528c2ecf20Sopenharmony_ci					       &psy_cfg);
2538c2ecf20Sopenharmony_ci	if (IS_ERR(wdata->battery)) {
2548c2ecf20Sopenharmony_ci		hid_err(wdata->hdev, "cannot register battery device\n");
2558c2ecf20Sopenharmony_ci		ret = PTR_ERR(wdata->battery);
2568c2ecf20Sopenharmony_ci		goto err_free;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	power_supply_powers(wdata->battery, &wdata->hdev->dev);
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cierr_free:
2638c2ecf20Sopenharmony_ci	kfree(wdata->battery_desc.name);
2648c2ecf20Sopenharmony_ci	wdata->battery_desc.name = NULL;
2658c2ecf20Sopenharmony_ci	return ret;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void wiimod_battery_remove(const struct wiimod_ops *ops,
2698c2ecf20Sopenharmony_ci				  struct wiimote_data *wdata)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	if (!wdata->battery_desc.name)
2728c2ecf20Sopenharmony_ci		return;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	power_supply_unregister(wdata->battery);
2758c2ecf20Sopenharmony_ci	kfree(wdata->battery_desc.name);
2768c2ecf20Sopenharmony_ci	wdata->battery_desc.name = NULL;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_battery = {
2808c2ecf20Sopenharmony_ci	.flags = 0,
2818c2ecf20Sopenharmony_ci	.arg = 0,
2828c2ecf20Sopenharmony_ci	.probe = wiimod_battery_probe,
2838c2ecf20Sopenharmony_ci	.remove = wiimod_battery_remove,
2848c2ecf20Sopenharmony_ci};
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/*
2878c2ecf20Sopenharmony_ci * LED
2888c2ecf20Sopenharmony_ci * 0 to 4 player LEDs are supported by devices. The "arg" field of the
2898c2ecf20Sopenharmony_ci * wiimod_ops structure specifies which LED this module controls. This allows
2908c2ecf20Sopenharmony_ci * to register a limited number of LEDs.
2918c2ecf20Sopenharmony_ci * State is managed by wiimote core.
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic enum led_brightness wiimod_led_get(struct led_classdev *led_dev)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct device *dev = led_dev->dev->parent;
2978c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
2988c2ecf20Sopenharmony_ci	int i;
2998c2ecf20Sopenharmony_ci	unsigned long flags;
3008c2ecf20Sopenharmony_ci	bool value = false;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	for (i = 0; i < 4; ++i) {
3038c2ecf20Sopenharmony_ci		if (wdata->leds[i] == led_dev) {
3048c2ecf20Sopenharmony_ci			spin_lock_irqsave(&wdata->state.lock, flags);
3058c2ecf20Sopenharmony_ci			value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1);
3068c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&wdata->state.lock, flags);
3078c2ecf20Sopenharmony_ci			break;
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return value ? LED_FULL : LED_OFF;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic void wiimod_led_set(struct led_classdev *led_dev,
3158c2ecf20Sopenharmony_ci			   enum led_brightness value)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct device *dev = led_dev->dev->parent;
3188c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
3198c2ecf20Sopenharmony_ci	int i;
3208c2ecf20Sopenharmony_ci	unsigned long flags;
3218c2ecf20Sopenharmony_ci	__u8 state, flag;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	for (i = 0; i < 4; ++i) {
3248c2ecf20Sopenharmony_ci		if (wdata->leds[i] == led_dev) {
3258c2ecf20Sopenharmony_ci			flag = WIIPROTO_FLAG_LED(i + 1);
3268c2ecf20Sopenharmony_ci			spin_lock_irqsave(&wdata->state.lock, flags);
3278c2ecf20Sopenharmony_ci			state = wdata->state.flags;
3288c2ecf20Sopenharmony_ci			if (value == LED_OFF)
3298c2ecf20Sopenharmony_ci				wiiproto_req_leds(wdata, state & ~flag);
3308c2ecf20Sopenharmony_ci			else
3318c2ecf20Sopenharmony_ci				wiiproto_req_leds(wdata, state | flag);
3328c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&wdata->state.lock, flags);
3338c2ecf20Sopenharmony_ci			break;
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int wiimod_led_probe(const struct wiimod_ops *ops,
3398c2ecf20Sopenharmony_ci			    struct wiimote_data *wdata)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct device *dev = &wdata->hdev->dev;
3428c2ecf20Sopenharmony_ci	size_t namesz = strlen(dev_name(dev)) + 9;
3438c2ecf20Sopenharmony_ci	struct led_classdev *led;
3448c2ecf20Sopenharmony_ci	unsigned long flags;
3458c2ecf20Sopenharmony_ci	char *name;
3468c2ecf20Sopenharmony_ci	int ret;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
3498c2ecf20Sopenharmony_ci	if (!led)
3508c2ecf20Sopenharmony_ci		return -ENOMEM;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	name = (void*)&led[1];
3538c2ecf20Sopenharmony_ci	snprintf(name, namesz, "%s:blue:p%lu", dev_name(dev), ops->arg);
3548c2ecf20Sopenharmony_ci	led->name = name;
3558c2ecf20Sopenharmony_ci	led->brightness = 0;
3568c2ecf20Sopenharmony_ci	led->max_brightness = 1;
3578c2ecf20Sopenharmony_ci	led->brightness_get = wiimod_led_get;
3588c2ecf20Sopenharmony_ci	led->brightness_set = wiimod_led_set;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	wdata->leds[ops->arg] = led;
3618c2ecf20Sopenharmony_ci	ret = led_classdev_register(dev, led);
3628c2ecf20Sopenharmony_ci	if (ret)
3638c2ecf20Sopenharmony_ci		goto err_free;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* enable LED1 to stop initial LED-blinking */
3668c2ecf20Sopenharmony_ci	if (ops->arg == 0) {
3678c2ecf20Sopenharmony_ci		spin_lock_irqsave(&wdata->state.lock, flags);
3688c2ecf20Sopenharmony_ci		wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
3698c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&wdata->state.lock, flags);
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return 0;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cierr_free:
3758c2ecf20Sopenharmony_ci	wdata->leds[ops->arg] = NULL;
3768c2ecf20Sopenharmony_ci	kfree(led);
3778c2ecf20Sopenharmony_ci	return ret;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void wiimod_led_remove(const struct wiimod_ops *ops,
3818c2ecf20Sopenharmony_ci			      struct wiimote_data *wdata)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	if (!wdata->leds[ops->arg])
3848c2ecf20Sopenharmony_ci		return;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	led_classdev_unregister(wdata->leds[ops->arg]);
3878c2ecf20Sopenharmony_ci	kfree(wdata->leds[ops->arg]);
3888c2ecf20Sopenharmony_ci	wdata->leds[ops->arg] = NULL;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_leds[4] = {
3928c2ecf20Sopenharmony_ci	{
3938c2ecf20Sopenharmony_ci		.flags = 0,
3948c2ecf20Sopenharmony_ci		.arg = 0,
3958c2ecf20Sopenharmony_ci		.probe = wiimod_led_probe,
3968c2ecf20Sopenharmony_ci		.remove = wiimod_led_remove,
3978c2ecf20Sopenharmony_ci	},
3988c2ecf20Sopenharmony_ci	{
3998c2ecf20Sopenharmony_ci		.flags = 0,
4008c2ecf20Sopenharmony_ci		.arg = 1,
4018c2ecf20Sopenharmony_ci		.probe = wiimod_led_probe,
4028c2ecf20Sopenharmony_ci		.remove = wiimod_led_remove,
4038c2ecf20Sopenharmony_ci	},
4048c2ecf20Sopenharmony_ci	{
4058c2ecf20Sopenharmony_ci		.flags = 0,
4068c2ecf20Sopenharmony_ci		.arg = 2,
4078c2ecf20Sopenharmony_ci		.probe = wiimod_led_probe,
4088c2ecf20Sopenharmony_ci		.remove = wiimod_led_remove,
4098c2ecf20Sopenharmony_ci	},
4108c2ecf20Sopenharmony_ci	{
4118c2ecf20Sopenharmony_ci		.flags = 0,
4128c2ecf20Sopenharmony_ci		.arg = 3,
4138c2ecf20Sopenharmony_ci		.probe = wiimod_led_probe,
4148c2ecf20Sopenharmony_ci		.remove = wiimod_led_remove,
4158c2ecf20Sopenharmony_ci	},
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci/*
4198c2ecf20Sopenharmony_ci * Accelerometer
4208c2ecf20Sopenharmony_ci * 3 axis accelerometer data is part of nearly all DRMs. If not supported by a
4218c2ecf20Sopenharmony_ci * device, it's mostly cleared to 0. This module parses this data and provides
4228c2ecf20Sopenharmony_ci * it via a separate input device.
4238c2ecf20Sopenharmony_ci */
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void wiimod_accel_in_accel(struct wiimote_data *wdata,
4268c2ecf20Sopenharmony_ci				  const __u8 *accel)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	__u16 x, y, z;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
4318c2ecf20Sopenharmony_ci		return;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/*
4348c2ecf20Sopenharmony_ci	 * payload is: BB BB XX YY ZZ
4358c2ecf20Sopenharmony_ci	 * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
4368c2ecf20Sopenharmony_ci	 * contain the upper 8 bits of each value. The lower 2 bits are
4378c2ecf20Sopenharmony_ci	 * contained in the buttons data BB BB.
4388c2ecf20Sopenharmony_ci	 * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
4398c2ecf20Sopenharmony_ci	 * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
4408c2ecf20Sopenharmony_ci	 * accel value and bit 6 is the second bit of the Z value.
4418c2ecf20Sopenharmony_ci	 * The first bit of Y and Z values is not available and always set to 0.
4428c2ecf20Sopenharmony_ci	 * 0x200 is returned on no movement.
4438c2ecf20Sopenharmony_ci	 */
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	x = accel[2] << 2;
4468c2ecf20Sopenharmony_ci	y = accel[3] << 2;
4478c2ecf20Sopenharmony_ci	z = accel[4] << 2;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	x |= (accel[0] >> 5) & 0x3;
4508c2ecf20Sopenharmony_ci	y |= (accel[1] >> 4) & 0x2;
4518c2ecf20Sopenharmony_ci	z |= (accel[1] >> 5) & 0x2;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	input_report_abs(wdata->accel, ABS_RX, x - 0x200);
4548c2ecf20Sopenharmony_ci	input_report_abs(wdata->accel, ABS_RY, y - 0x200);
4558c2ecf20Sopenharmony_ci	input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
4568c2ecf20Sopenharmony_ci	input_sync(wdata->accel);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic int wiimod_accel_open(struct input_dev *dev)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
4628c2ecf20Sopenharmony_ci	unsigned long flags;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
4658c2ecf20Sopenharmony_ci	wiiproto_req_accel(wdata, true);
4668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic void wiimod_accel_close(struct input_dev *dev)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
4748c2ecf20Sopenharmony_ci	unsigned long flags;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
4778c2ecf20Sopenharmony_ci	wiiproto_req_accel(wdata, false);
4788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic int wiimod_accel_probe(const struct wiimod_ops *ops,
4828c2ecf20Sopenharmony_ci			      struct wiimote_data *wdata)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	int ret;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	wdata->accel = input_allocate_device();
4878c2ecf20Sopenharmony_ci	if (!wdata->accel)
4888c2ecf20Sopenharmony_ci		return -ENOMEM;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->accel, wdata);
4918c2ecf20Sopenharmony_ci	wdata->accel->open = wiimod_accel_open;
4928c2ecf20Sopenharmony_ci	wdata->accel->close = wiimod_accel_close;
4938c2ecf20Sopenharmony_ci	wdata->accel->dev.parent = &wdata->hdev->dev;
4948c2ecf20Sopenharmony_ci	wdata->accel->id.bustype = wdata->hdev->bus;
4958c2ecf20Sopenharmony_ci	wdata->accel->id.vendor = wdata->hdev->vendor;
4968c2ecf20Sopenharmony_ci	wdata->accel->id.product = wdata->hdev->product;
4978c2ecf20Sopenharmony_ci	wdata->accel->id.version = wdata->hdev->version;
4988c2ecf20Sopenharmony_ci	wdata->accel->name = WIIMOTE_NAME " Accelerometer";
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->accel->evbit);
5018c2ecf20Sopenharmony_ci	set_bit(ABS_RX, wdata->accel->absbit);
5028c2ecf20Sopenharmony_ci	set_bit(ABS_RY, wdata->accel->absbit);
5038c2ecf20Sopenharmony_ci	set_bit(ABS_RZ, wdata->accel->absbit);
5048c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
5058c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
5068c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->accel);
5098c2ecf20Sopenharmony_ci	if (ret) {
5108c2ecf20Sopenharmony_ci		hid_err(wdata->hdev, "cannot register input device\n");
5118c2ecf20Sopenharmony_ci		goto err_free;
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cierr_free:
5178c2ecf20Sopenharmony_ci	input_free_device(wdata->accel);
5188c2ecf20Sopenharmony_ci	wdata->accel = NULL;
5198c2ecf20Sopenharmony_ci	return ret;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void wiimod_accel_remove(const struct wiimod_ops *ops,
5238c2ecf20Sopenharmony_ci				struct wiimote_data *wdata)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	if (!wdata->accel)
5268c2ecf20Sopenharmony_ci		return;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	input_unregister_device(wdata->accel);
5298c2ecf20Sopenharmony_ci	wdata->accel = NULL;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_accel = {
5338c2ecf20Sopenharmony_ci	.flags = 0,
5348c2ecf20Sopenharmony_ci	.arg = 0,
5358c2ecf20Sopenharmony_ci	.probe = wiimod_accel_probe,
5368c2ecf20Sopenharmony_ci	.remove = wiimod_accel_remove,
5378c2ecf20Sopenharmony_ci	.in_accel = wiimod_accel_in_accel,
5388c2ecf20Sopenharmony_ci};
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci/*
5418c2ecf20Sopenharmony_ci * IR Cam
5428c2ecf20Sopenharmony_ci * Up to 4 IR sources can be tracked by a normal Wii Remote. The IR cam needs
5438c2ecf20Sopenharmony_ci * to be initialized with a fairly complex procedure and consumes a lot of
5448c2ecf20Sopenharmony_ci * power. Therefore, as long as no application uses the IR input device, it is
5458c2ecf20Sopenharmony_ci * kept offline.
5468c2ecf20Sopenharmony_ci * Nearly no other device than the normal Wii Remotes supports the IR cam so
5478c2ecf20Sopenharmony_ci * you can disable this module for these devices.
5488c2ecf20Sopenharmony_ci */
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void wiimod_ir_in_ir(struct wiimote_data *wdata, const __u8 *ir,
5518c2ecf20Sopenharmony_ci			    bool packed, unsigned int id)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	__u16 x, y;
5548c2ecf20Sopenharmony_ci	__u8 xid, yid;
5558c2ecf20Sopenharmony_ci	bool sync = false;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
5588c2ecf20Sopenharmony_ci		return;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	switch (id) {
5618c2ecf20Sopenharmony_ci	case 0:
5628c2ecf20Sopenharmony_ci		xid = ABS_HAT0X;
5638c2ecf20Sopenharmony_ci		yid = ABS_HAT0Y;
5648c2ecf20Sopenharmony_ci		break;
5658c2ecf20Sopenharmony_ci	case 1:
5668c2ecf20Sopenharmony_ci		xid = ABS_HAT1X;
5678c2ecf20Sopenharmony_ci		yid = ABS_HAT1Y;
5688c2ecf20Sopenharmony_ci		break;
5698c2ecf20Sopenharmony_ci	case 2:
5708c2ecf20Sopenharmony_ci		xid = ABS_HAT2X;
5718c2ecf20Sopenharmony_ci		yid = ABS_HAT2Y;
5728c2ecf20Sopenharmony_ci		break;
5738c2ecf20Sopenharmony_ci	case 3:
5748c2ecf20Sopenharmony_ci		xid = ABS_HAT3X;
5758c2ecf20Sopenharmony_ci		yid = ABS_HAT3Y;
5768c2ecf20Sopenharmony_ci		sync = true;
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci	default:
5798c2ecf20Sopenharmony_ci		return;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/*
5838c2ecf20Sopenharmony_ci	 * Basic IR data is encoded into 3 bytes. The first two bytes are the
5848c2ecf20Sopenharmony_ci	 * lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits
5858c2ecf20Sopenharmony_ci	 * of both.
5868c2ecf20Sopenharmony_ci	 * If data is packed, then the 3rd byte is put first and slightly
5878c2ecf20Sopenharmony_ci	 * reordered. This allows to interleave packed and non-packed data to
5888c2ecf20Sopenharmony_ci	 * have two IR sets in 5 bytes instead of 6.
5898c2ecf20Sopenharmony_ci	 * The resulting 10bit X/Y values are passed to the ABS_HAT? input dev.
5908c2ecf20Sopenharmony_ci	 */
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	if (packed) {
5938c2ecf20Sopenharmony_ci		x = ir[1] | ((ir[0] & 0x03) << 8);
5948c2ecf20Sopenharmony_ci		y = ir[2] | ((ir[0] & 0x0c) << 6);
5958c2ecf20Sopenharmony_ci	} else {
5968c2ecf20Sopenharmony_ci		x = ir[0] | ((ir[2] & 0x30) << 4);
5978c2ecf20Sopenharmony_ci		y = ir[1] | ((ir[2] & 0xc0) << 2);
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	input_report_abs(wdata->ir, xid, x);
6018c2ecf20Sopenharmony_ci	input_report_abs(wdata->ir, yid, y);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	if (sync)
6048c2ecf20Sopenharmony_ci		input_sync(wdata->ir);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int wiimod_ir_change(struct wiimote_data *wdata, __u16 mode)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	int ret;
6108c2ecf20Sopenharmony_ci	unsigned long flags;
6118c2ecf20Sopenharmony_ci	__u8 format = 0;
6128c2ecf20Sopenharmony_ci	static const __u8 data_enable[] = { 0x01 };
6138c2ecf20Sopenharmony_ci	static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01,
6148c2ecf20Sopenharmony_ci						0x00, 0xaa, 0x00, 0x64 };
6158c2ecf20Sopenharmony_ci	static const __u8 data_sens2[] = { 0x63, 0x03 };
6168c2ecf20Sopenharmony_ci	static const __u8 data_fin[] = { 0x08 };
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
6218c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&wdata->state.lock, flags);
6228c2ecf20Sopenharmony_ci		return 0;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (mode == 0) {
6268c2ecf20Sopenharmony_ci		wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
6278c2ecf20Sopenharmony_ci		wiiproto_req_ir1(wdata, 0);
6288c2ecf20Sopenharmony_ci		wiiproto_req_ir2(wdata, 0);
6298c2ecf20Sopenharmony_ci		wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
6308c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&wdata->state.lock, flags);
6318c2ecf20Sopenharmony_ci		return 0;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	ret = wiimote_cmd_acquire(wdata);
6378c2ecf20Sopenharmony_ci	if (ret)
6388c2ecf20Sopenharmony_ci		return ret;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* send PIXEL CLOCK ENABLE cmd first */
6418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
6428c2ecf20Sopenharmony_ci	wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0);
6438c2ecf20Sopenharmony_ci	wiiproto_req_ir1(wdata, 0x06);
6448c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	ret = wiimote_cmd_wait(wdata);
6478c2ecf20Sopenharmony_ci	if (ret)
6488c2ecf20Sopenharmony_ci		goto unlock;
6498c2ecf20Sopenharmony_ci	if (wdata->state.cmd_err) {
6508c2ecf20Sopenharmony_ci		ret = -EIO;
6518c2ecf20Sopenharmony_ci		goto unlock;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* enable IR LOGIC */
6558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
6568c2ecf20Sopenharmony_ci	wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0);
6578c2ecf20Sopenharmony_ci	wiiproto_req_ir2(wdata, 0x06);
6588c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	ret = wiimote_cmd_wait(wdata);
6618c2ecf20Sopenharmony_ci	if (ret)
6628c2ecf20Sopenharmony_ci		goto unlock;
6638c2ecf20Sopenharmony_ci	if (wdata->state.cmd_err) {
6648c2ecf20Sopenharmony_ci		ret = -EIO;
6658c2ecf20Sopenharmony_ci		goto unlock;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* enable IR cam but do not make it send data, yet */
6698c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xb00030, data_enable,
6708c2ecf20Sopenharmony_ci							sizeof(data_enable));
6718c2ecf20Sopenharmony_ci	if (ret)
6728c2ecf20Sopenharmony_ci		goto unlock;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* write first sensitivity block */
6758c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
6768c2ecf20Sopenharmony_ci							sizeof(data_sens1));
6778c2ecf20Sopenharmony_ci	if (ret)
6788c2ecf20Sopenharmony_ci		goto unlock;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* write second sensitivity block */
6818c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
6828c2ecf20Sopenharmony_ci							sizeof(data_sens2));
6838c2ecf20Sopenharmony_ci	if (ret)
6848c2ecf20Sopenharmony_ci		goto unlock;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* put IR cam into desired state */
6878c2ecf20Sopenharmony_ci	switch (mode) {
6888c2ecf20Sopenharmony_ci		case WIIPROTO_FLAG_IR_FULL:
6898c2ecf20Sopenharmony_ci			format = 5;
6908c2ecf20Sopenharmony_ci			break;
6918c2ecf20Sopenharmony_ci		case WIIPROTO_FLAG_IR_EXT:
6928c2ecf20Sopenharmony_ci			format = 3;
6938c2ecf20Sopenharmony_ci			break;
6948c2ecf20Sopenharmony_ci		case WIIPROTO_FLAG_IR_BASIC:
6958c2ecf20Sopenharmony_ci			format = 1;
6968c2ecf20Sopenharmony_ci			break;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format));
6998c2ecf20Sopenharmony_ci	if (ret)
7008c2ecf20Sopenharmony_ci		goto unlock;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* make IR cam send data */
7038c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin));
7048c2ecf20Sopenharmony_ci	if (ret)
7058c2ecf20Sopenharmony_ci		goto unlock;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* request new DRM mode compatible to IR mode */
7088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
7098c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
7108c2ecf20Sopenharmony_ci	wdata->state.flags |= mode & WIIPROTO_FLAGS_IR;
7118c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
7128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ciunlock:
7158c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
7168c2ecf20Sopenharmony_ci	return ret;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic int wiimod_ir_open(struct input_dev *dev)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	return wiimod_ir_change(wdata, WIIPROTO_FLAG_IR_BASIC);
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic void wiimod_ir_close(struct input_dev *dev)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	wiimod_ir_change(wdata, 0);
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic int wiimod_ir_probe(const struct wiimod_ops *ops,
7348c2ecf20Sopenharmony_ci			   struct wiimote_data *wdata)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	int ret;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	wdata->ir = input_allocate_device();
7398c2ecf20Sopenharmony_ci	if (!wdata->ir)
7408c2ecf20Sopenharmony_ci		return -ENOMEM;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->ir, wdata);
7438c2ecf20Sopenharmony_ci	wdata->ir->open = wiimod_ir_open;
7448c2ecf20Sopenharmony_ci	wdata->ir->close = wiimod_ir_close;
7458c2ecf20Sopenharmony_ci	wdata->ir->dev.parent = &wdata->hdev->dev;
7468c2ecf20Sopenharmony_ci	wdata->ir->id.bustype = wdata->hdev->bus;
7478c2ecf20Sopenharmony_ci	wdata->ir->id.vendor = wdata->hdev->vendor;
7488c2ecf20Sopenharmony_ci	wdata->ir->id.product = wdata->hdev->product;
7498c2ecf20Sopenharmony_ci	wdata->ir->id.version = wdata->hdev->version;
7508c2ecf20Sopenharmony_ci	wdata->ir->name = WIIMOTE_NAME " IR";
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->ir->evbit);
7538c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0X, wdata->ir->absbit);
7548c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0Y, wdata->ir->absbit);
7558c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1X, wdata->ir->absbit);
7568c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1Y, wdata->ir->absbit);
7578c2ecf20Sopenharmony_ci	set_bit(ABS_HAT2X, wdata->ir->absbit);
7588c2ecf20Sopenharmony_ci	set_bit(ABS_HAT2Y, wdata->ir->absbit);
7598c2ecf20Sopenharmony_ci	set_bit(ABS_HAT3X, wdata->ir->absbit);
7608c2ecf20Sopenharmony_ci	set_bit(ABS_HAT3Y, wdata->ir->absbit);
7618c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
7628c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
7638c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
7648c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
7658c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
7668c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
7678c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
7688c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->ir);
7718c2ecf20Sopenharmony_ci	if (ret) {
7728c2ecf20Sopenharmony_ci		hid_err(wdata->hdev, "cannot register input device\n");
7738c2ecf20Sopenharmony_ci		goto err_free;
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	return 0;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cierr_free:
7798c2ecf20Sopenharmony_ci	input_free_device(wdata->ir);
7808c2ecf20Sopenharmony_ci	wdata->ir = NULL;
7818c2ecf20Sopenharmony_ci	return ret;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic void wiimod_ir_remove(const struct wiimod_ops *ops,
7858c2ecf20Sopenharmony_ci			     struct wiimote_data *wdata)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	if (!wdata->ir)
7888c2ecf20Sopenharmony_ci		return;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	input_unregister_device(wdata->ir);
7918c2ecf20Sopenharmony_ci	wdata->ir = NULL;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_ir = {
7958c2ecf20Sopenharmony_ci	.flags = 0,
7968c2ecf20Sopenharmony_ci	.arg = 0,
7978c2ecf20Sopenharmony_ci	.probe = wiimod_ir_probe,
7988c2ecf20Sopenharmony_ci	.remove = wiimod_ir_remove,
7998c2ecf20Sopenharmony_ci	.in_ir = wiimod_ir_in_ir,
8008c2ecf20Sopenharmony_ci};
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci/*
8038c2ecf20Sopenharmony_ci * Nunchuk Extension
8048c2ecf20Sopenharmony_ci * The Nintendo Wii Nunchuk was the first official extension published by
8058c2ecf20Sopenharmony_ci * Nintendo. It provides two additional keys and a separate accelerometer. It
8068c2ecf20Sopenharmony_ci * can be hotplugged to standard Wii Remotes.
8078c2ecf20Sopenharmony_ci */
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_cienum wiimod_nunchuk_keys {
8108c2ecf20Sopenharmony_ci	WIIMOD_NUNCHUK_KEY_C,
8118c2ecf20Sopenharmony_ci	WIIMOD_NUNCHUK_KEY_Z,
8128c2ecf20Sopenharmony_ci	WIIMOD_NUNCHUK_KEY_NUM,
8138c2ecf20Sopenharmony_ci};
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic const __u16 wiimod_nunchuk_map[] = {
8168c2ecf20Sopenharmony_ci	BTN_C,		/* WIIMOD_NUNCHUK_KEY_C */
8178c2ecf20Sopenharmony_ci	BTN_Z,		/* WIIMOD_NUNCHUK_KEY_Z */
8188c2ecf20Sopenharmony_ci};
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic void wiimod_nunchuk_in_ext(struct wiimote_data *wdata, const __u8 *ext)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	__s16 x, y, z, bx, by;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	/*   Byte |   8    7 |  6    5 |  4    3 |  2 |  1  |
8258c2ecf20Sopenharmony_ci	 *   -----+----------+---------+---------+----+-----+
8268c2ecf20Sopenharmony_ci	 *    1   |              Button X <7:0>             |
8278c2ecf20Sopenharmony_ci	 *    2   |              Button Y <7:0>             |
8288c2ecf20Sopenharmony_ci	 *   -----+----------+---------+---------+----+-----+
8298c2ecf20Sopenharmony_ci	 *    3   |               Speed X <9:2>             |
8308c2ecf20Sopenharmony_ci	 *    4   |               Speed Y <9:2>             |
8318c2ecf20Sopenharmony_ci	 *    5   |               Speed Z <9:2>             |
8328c2ecf20Sopenharmony_ci	 *   -----+----------+---------+---------+----+-----+
8338c2ecf20Sopenharmony_ci	 *    6   | Z <1:0>  | Y <1:0> | X <1:0> | BC | BZ  |
8348c2ecf20Sopenharmony_ci	 *   -----+----------+---------+---------+----+-----+
8358c2ecf20Sopenharmony_ci	 * Button X/Y is the analog stick. Speed X, Y and Z are the
8368c2ecf20Sopenharmony_ci	 * accelerometer data in the same format as the wiimote's accelerometer.
8378c2ecf20Sopenharmony_ci	 * The 6th byte contains the LSBs of the accelerometer data.
8388c2ecf20Sopenharmony_ci	 * BC and BZ are the C and Z buttons: 0 means pressed
8398c2ecf20Sopenharmony_ci	 *
8408c2ecf20Sopenharmony_ci	 * If reported interleaved with motionp, then the layout changes. The
8418c2ecf20Sopenharmony_ci	 * 5th and 6th byte changes to:
8428c2ecf20Sopenharmony_ci	 *   -----+-----------------------------------+-----+
8438c2ecf20Sopenharmony_ci	 *    5   |            Speed Z <9:3>          | EXT |
8448c2ecf20Sopenharmony_ci	 *   -----+--------+-----+-----+----+----+----+-----+
8458c2ecf20Sopenharmony_ci	 *    6   |Z <2:1> |Y <1>|X <1>| BC | BZ | 0  |  0  |
8468c2ecf20Sopenharmony_ci	 *   -----+--------+-----+-----+----+----+----+-----+
8478c2ecf20Sopenharmony_ci	 * All three accelerometer values lose their LSB. The other data is
8488c2ecf20Sopenharmony_ci	 * still available but slightly moved.
8498c2ecf20Sopenharmony_ci	 *
8508c2ecf20Sopenharmony_ci	 * Center data for button values is 128. Center value for accelerometer
8518c2ecf20Sopenharmony_ci	 * values it 512 / 0x200
8528c2ecf20Sopenharmony_ci	 */
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	bx = ext[0];
8558c2ecf20Sopenharmony_ci	by = ext[1];
8568c2ecf20Sopenharmony_ci	bx -= 128;
8578c2ecf20Sopenharmony_ci	by -= 128;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	x = ext[2] << 2;
8608c2ecf20Sopenharmony_ci	y = ext[3] << 2;
8618c2ecf20Sopenharmony_ci	z = ext[4] << 2;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
8648c2ecf20Sopenharmony_ci		x |= (ext[5] >> 3) & 0x02;
8658c2ecf20Sopenharmony_ci		y |= (ext[5] >> 4) & 0x02;
8668c2ecf20Sopenharmony_ci		z &= ~0x4;
8678c2ecf20Sopenharmony_ci		z |= (ext[5] >> 5) & 0x06;
8688c2ecf20Sopenharmony_ci	} else {
8698c2ecf20Sopenharmony_ci		x |= (ext[5] >> 2) & 0x03;
8708c2ecf20Sopenharmony_ci		y |= (ext[5] >> 4) & 0x03;
8718c2ecf20Sopenharmony_ci		z |= (ext[5] >> 6) & 0x03;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	x -= 0x200;
8758c2ecf20Sopenharmony_ci	y -= 0x200;
8768c2ecf20Sopenharmony_ci	z -= 0x200;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT0X, bx);
8798c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT0Y, by);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_RX, x);
8828c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_RY, y);
8838c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_RZ, z);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
8868c2ecf20Sopenharmony_ci		input_report_key(wdata->extension.input,
8878c2ecf20Sopenharmony_ci			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_Z],
8888c2ecf20Sopenharmony_ci			!(ext[5] & 0x04));
8898c2ecf20Sopenharmony_ci		input_report_key(wdata->extension.input,
8908c2ecf20Sopenharmony_ci			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_C],
8918c2ecf20Sopenharmony_ci			!(ext[5] & 0x08));
8928c2ecf20Sopenharmony_ci	} else {
8938c2ecf20Sopenharmony_ci		input_report_key(wdata->extension.input,
8948c2ecf20Sopenharmony_ci			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_Z],
8958c2ecf20Sopenharmony_ci			!(ext[5] & 0x01));
8968c2ecf20Sopenharmony_ci		input_report_key(wdata->extension.input,
8978c2ecf20Sopenharmony_ci			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_C],
8988c2ecf20Sopenharmony_ci			!(ext[5] & 0x02));
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_cistatic int wiimod_nunchuk_open(struct input_dev *dev)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
9078c2ecf20Sopenharmony_ci	unsigned long flags;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
9108c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
9118c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
9128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	return 0;
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic void wiimod_nunchuk_close(struct input_dev *dev)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
9208c2ecf20Sopenharmony_ci	unsigned long flags;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
9238c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
9248c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
9258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_cistatic int wiimod_nunchuk_probe(const struct wiimod_ops *ops,
9298c2ecf20Sopenharmony_ci				struct wiimote_data *wdata)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	int ret, i;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	wdata->extension.input = input_allocate_device();
9348c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
9358c2ecf20Sopenharmony_ci		return -ENOMEM;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->extension.input, wdata);
9388c2ecf20Sopenharmony_ci	wdata->extension.input->open = wiimod_nunchuk_open;
9398c2ecf20Sopenharmony_ci	wdata->extension.input->close = wiimod_nunchuk_close;
9408c2ecf20Sopenharmony_ci	wdata->extension.input->dev.parent = &wdata->hdev->dev;
9418c2ecf20Sopenharmony_ci	wdata->extension.input->id.bustype = wdata->hdev->bus;
9428c2ecf20Sopenharmony_ci	wdata->extension.input->id.vendor = wdata->hdev->vendor;
9438c2ecf20Sopenharmony_ci	wdata->extension.input->id.product = wdata->hdev->product;
9448c2ecf20Sopenharmony_ci	wdata->extension.input->id.version = wdata->hdev->version;
9458c2ecf20Sopenharmony_ci	wdata->extension.input->name = WIIMOTE_NAME " Nunchuk";
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->extension.input->evbit);
9488c2ecf20Sopenharmony_ci	for (i = 0; i < WIIMOD_NUNCHUK_KEY_NUM; ++i)
9498c2ecf20Sopenharmony_ci		set_bit(wiimod_nunchuk_map[i],
9508c2ecf20Sopenharmony_ci			wdata->extension.input->keybit);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->extension.input->evbit);
9538c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
9548c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
9558c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
9568c2ecf20Sopenharmony_ci			     ABS_HAT0X, -120, 120, 2, 4);
9578c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
9588c2ecf20Sopenharmony_ci			     ABS_HAT0Y, -120, 120, 2, 4);
9598c2ecf20Sopenharmony_ci	set_bit(ABS_RX, wdata->extension.input->absbit);
9608c2ecf20Sopenharmony_ci	set_bit(ABS_RY, wdata->extension.input->absbit);
9618c2ecf20Sopenharmony_ci	set_bit(ABS_RZ, wdata->extension.input->absbit);
9628c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
9638c2ecf20Sopenharmony_ci			     ABS_RX, -500, 500, 2, 4);
9648c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
9658c2ecf20Sopenharmony_ci			     ABS_RY, -500, 500, 2, 4);
9668c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
9678c2ecf20Sopenharmony_ci			     ABS_RZ, -500, 500, 2, 4);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->extension.input);
9708c2ecf20Sopenharmony_ci	if (ret)
9718c2ecf20Sopenharmony_ci		goto err_free;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	return 0;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cierr_free:
9768c2ecf20Sopenharmony_ci	input_free_device(wdata->extension.input);
9778c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
9788c2ecf20Sopenharmony_ci	return ret;
9798c2ecf20Sopenharmony_ci}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_cistatic void wiimod_nunchuk_remove(const struct wiimod_ops *ops,
9828c2ecf20Sopenharmony_ci				  struct wiimote_data *wdata)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
9858c2ecf20Sopenharmony_ci		return;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	input_unregister_device(wdata->extension.input);
9888c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_nunchuk = {
9928c2ecf20Sopenharmony_ci	.flags = 0,
9938c2ecf20Sopenharmony_ci	.arg = 0,
9948c2ecf20Sopenharmony_ci	.probe = wiimod_nunchuk_probe,
9958c2ecf20Sopenharmony_ci	.remove = wiimod_nunchuk_remove,
9968c2ecf20Sopenharmony_ci	.in_ext = wiimod_nunchuk_in_ext,
9978c2ecf20Sopenharmony_ci};
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci/*
10008c2ecf20Sopenharmony_ci * Classic Controller
10018c2ecf20Sopenharmony_ci * Another official extension from Nintendo. It provides a classic
10028c2ecf20Sopenharmony_ci * gamecube-like controller that can be hotplugged on the Wii Remote.
10038c2ecf20Sopenharmony_ci * It has several hardware buttons and switches that are all reported via
10048c2ecf20Sopenharmony_ci * a normal extension device.
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cienum wiimod_classic_keys {
10088c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_A,
10098c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_B,
10108c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_X,
10118c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_Y,
10128c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_ZL,
10138c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_ZR,
10148c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_PLUS,
10158c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_MINUS,
10168c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_HOME,
10178c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_LEFT,
10188c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_RIGHT,
10198c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_UP,
10208c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_DOWN,
10218c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_LT,
10228c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_RT,
10238c2ecf20Sopenharmony_ci	WIIMOD_CLASSIC_KEY_NUM,
10248c2ecf20Sopenharmony_ci};
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic const __u16 wiimod_classic_map[] = {
10278c2ecf20Sopenharmony_ci	BTN_A,		/* WIIMOD_CLASSIC_KEY_A */
10288c2ecf20Sopenharmony_ci	BTN_B,		/* WIIMOD_CLASSIC_KEY_B */
10298c2ecf20Sopenharmony_ci	BTN_X,		/* WIIMOD_CLASSIC_KEY_X */
10308c2ecf20Sopenharmony_ci	BTN_Y,		/* WIIMOD_CLASSIC_KEY_Y */
10318c2ecf20Sopenharmony_ci	BTN_TL2,	/* WIIMOD_CLASSIC_KEY_ZL */
10328c2ecf20Sopenharmony_ci	BTN_TR2,	/* WIIMOD_CLASSIC_KEY_ZR */
10338c2ecf20Sopenharmony_ci	KEY_NEXT,	/* WIIMOD_CLASSIC_KEY_PLUS */
10348c2ecf20Sopenharmony_ci	KEY_PREVIOUS,	/* WIIMOD_CLASSIC_KEY_MINUS */
10358c2ecf20Sopenharmony_ci	BTN_MODE,	/* WIIMOD_CLASSIC_KEY_HOME */
10368c2ecf20Sopenharmony_ci	KEY_LEFT,	/* WIIMOD_CLASSIC_KEY_LEFT */
10378c2ecf20Sopenharmony_ci	KEY_RIGHT,	/* WIIMOD_CLASSIC_KEY_RIGHT */
10388c2ecf20Sopenharmony_ci	KEY_UP,		/* WIIMOD_CLASSIC_KEY_UP */
10398c2ecf20Sopenharmony_ci	KEY_DOWN,	/* WIIMOD_CLASSIC_KEY_DOWN */
10408c2ecf20Sopenharmony_ci	BTN_TL,		/* WIIMOD_CLASSIC_KEY_LT */
10418c2ecf20Sopenharmony_ci	BTN_TR,		/* WIIMOD_CLASSIC_KEY_RT */
10428c2ecf20Sopenharmony_ci};
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	__s8 rx, ry, lx, ly, lt, rt;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
10498c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10508c2ecf20Sopenharmony_ci	 *    1   | RX <5:4>  |              LX <5:0>             |
10518c2ecf20Sopenharmony_ci	 *    2   | RX <3:2>  |              LY <5:0>             |
10528c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----------------------------+
10538c2ecf20Sopenharmony_ci	 *    3   |RX<1>| LT <5:4>  |         RY <5:1>            |
10548c2ecf20Sopenharmony_ci	 *   -----+-----+-----------+-----------------------------+
10558c2ecf20Sopenharmony_ci	 *    4   |     LT <3:1>    |         RT <5:1>            |
10568c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10578c2ecf20Sopenharmony_ci	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT |  1  |
10588c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10598c2ecf20Sopenharmony_ci	 *    6   | BZL | BB  | BY  | BA  | BX  | BZR | BDL | BDU |
10608c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10618c2ecf20Sopenharmony_ci	 * All buttons are 0 if pressed
10628c2ecf20Sopenharmony_ci	 * RX and RY are right analog stick
10638c2ecf20Sopenharmony_ci	 * LX and LY are left analog stick
10648c2ecf20Sopenharmony_ci	 * LT is left trigger, RT is right trigger
10658c2ecf20Sopenharmony_ci	 * BLT is 0 if left trigger is fully pressed
10668c2ecf20Sopenharmony_ci	 * BRT is 0 if right trigger is fully pressed
10678c2ecf20Sopenharmony_ci	 * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
10688c2ecf20Sopenharmony_ci	 * BZL is left Z button and BZR is right Z button
10698c2ecf20Sopenharmony_ci	 * B-, BH, B+ are +, HOME and - buttons
10708c2ecf20Sopenharmony_ci	 * BB, BY, BA, BX are A, B, X, Y buttons
10718c2ecf20Sopenharmony_ci	 * LSB of RX, RY, LT, and RT are not transmitted and always 0.
10728c2ecf20Sopenharmony_ci	 *
10738c2ecf20Sopenharmony_ci	 * With motionp enabled it changes slightly to this:
10748c2ecf20Sopenharmony_ci	 *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
10758c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10768c2ecf20Sopenharmony_ci	 *    1   | RX <5:4>  |          LX <5:1>           | BDU |
10778c2ecf20Sopenharmony_ci	 *    2   | RX <3:2>  |          LY <5:1>           | BDL |
10788c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----------------------+-----+
10798c2ecf20Sopenharmony_ci	 *    3   |RX<1>| LT <5:4>  |         RY <5:1>            |
10808c2ecf20Sopenharmony_ci	 *   -----+-----+-----------+-----------------------------+
10818c2ecf20Sopenharmony_ci	 *    4   |     LT <3:1>    |         RT <5:1>            |
10828c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10838c2ecf20Sopenharmony_ci	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT | EXT |
10848c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10858c2ecf20Sopenharmony_ci	 *    6   | BZL | BB  | BY  | BA  | BX  | BZR |  0  |  0  |
10868c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
10878c2ecf20Sopenharmony_ci	 * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest
10888c2ecf20Sopenharmony_ci	 * is the same as before.
10898c2ecf20Sopenharmony_ci	 */
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	static const s8 digital_to_analog[3] = {0x20, 0, -0x20};
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
10948c2ecf20Sopenharmony_ci		if (wiimote_dpad_as_analog) {
10958c2ecf20Sopenharmony_ci			lx = digital_to_analog[1 - !(ext[4] & 0x80)
10968c2ecf20Sopenharmony_ci				+ !(ext[1] & 0x01)];
10978c2ecf20Sopenharmony_ci			ly = digital_to_analog[1 - !(ext[4] & 0x40)
10988c2ecf20Sopenharmony_ci				+ !(ext[0] & 0x01)];
10998c2ecf20Sopenharmony_ci		} else {
11008c2ecf20Sopenharmony_ci			lx = (ext[0] & 0x3e) - 0x20;
11018c2ecf20Sopenharmony_ci			ly = (ext[1] & 0x3e) - 0x20;
11028c2ecf20Sopenharmony_ci		}
11038c2ecf20Sopenharmony_ci	} else {
11048c2ecf20Sopenharmony_ci		if (wiimote_dpad_as_analog) {
11058c2ecf20Sopenharmony_ci			lx = digital_to_analog[1 - !(ext[4] & 0x80)
11068c2ecf20Sopenharmony_ci				+ !(ext[5] & 0x02)];
11078c2ecf20Sopenharmony_ci			ly = digital_to_analog[1 - !(ext[4] & 0x40)
11088c2ecf20Sopenharmony_ci				+ !(ext[5] & 0x01)];
11098c2ecf20Sopenharmony_ci		} else {
11108c2ecf20Sopenharmony_ci			lx = (ext[0] & 0x3f) - 0x20;
11118c2ecf20Sopenharmony_ci			ly = (ext[1] & 0x3f) - 0x20;
11128c2ecf20Sopenharmony_ci		}
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	rx = (ext[0] >> 3) & 0x18;
11168c2ecf20Sopenharmony_ci	rx |= (ext[1] >> 5) & 0x06;
11178c2ecf20Sopenharmony_ci	rx |= (ext[2] >> 7) & 0x01;
11188c2ecf20Sopenharmony_ci	ry = ext[2] & 0x1f;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	rt = ext[3] & 0x1f;
11218c2ecf20Sopenharmony_ci	lt = (ext[2] >> 2) & 0x18;
11228c2ecf20Sopenharmony_ci	lt |= (ext[3] >> 5) & 0x07;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	rx <<= 1;
11258c2ecf20Sopenharmony_ci	ry <<= 1;
11268c2ecf20Sopenharmony_ci	rt <<= 1;
11278c2ecf20Sopenharmony_ci	lt <<= 1;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT1X, lx);
11308c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT1Y, ly);
11318c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20);
11328c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20);
11338c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT3X, rt);
11348c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT3Y, lt);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11378c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_LT],
11388c2ecf20Sopenharmony_ci			 !(ext[4] & 0x20));
11398c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11408c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_MINUS],
11418c2ecf20Sopenharmony_ci			 !(ext[4] & 0x10));
11428c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11438c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_HOME],
11448c2ecf20Sopenharmony_ci			 !(ext[4] & 0x08));
11458c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11468c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_PLUS],
11478c2ecf20Sopenharmony_ci			 !(ext[4] & 0x04));
11488c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11498c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_RT],
11508c2ecf20Sopenharmony_ci			 !(ext[4] & 0x02));
11518c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11528c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZL],
11538c2ecf20Sopenharmony_ci			 !(ext[5] & 0x80));
11548c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11558c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_B],
11568c2ecf20Sopenharmony_ci			 !(ext[5] & 0x40));
11578c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11588c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_Y],
11598c2ecf20Sopenharmony_ci			 !(ext[5] & 0x20));
11608c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11618c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_A],
11628c2ecf20Sopenharmony_ci			 !(ext[5] & 0x10));
11638c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11648c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_X],
11658c2ecf20Sopenharmony_ci			 !(ext[5] & 0x08));
11668c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
11678c2ecf20Sopenharmony_ci			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZR],
11688c2ecf20Sopenharmony_ci			 !(ext[5] & 0x04));
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	if (!wiimote_dpad_as_analog) {
11718c2ecf20Sopenharmony_ci		input_report_key(wdata->extension.input,
11728c2ecf20Sopenharmony_ci				 wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
11738c2ecf20Sopenharmony_ci				 !(ext[4] & 0x80));
11748c2ecf20Sopenharmony_ci		input_report_key(wdata->extension.input,
11758c2ecf20Sopenharmony_ci				 wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN],
11768c2ecf20Sopenharmony_ci				 !(ext[4] & 0x40));
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
11798c2ecf20Sopenharmony_ci			input_report_key(wdata->extension.input,
11808c2ecf20Sopenharmony_ci				 wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
11818c2ecf20Sopenharmony_ci				 !(ext[1] & 0x01));
11828c2ecf20Sopenharmony_ci			input_report_key(wdata->extension.input,
11838c2ecf20Sopenharmony_ci				 wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
11848c2ecf20Sopenharmony_ci				 !(ext[0] & 0x01));
11858c2ecf20Sopenharmony_ci		} else {
11868c2ecf20Sopenharmony_ci			input_report_key(wdata->extension.input,
11878c2ecf20Sopenharmony_ci				 wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
11888c2ecf20Sopenharmony_ci				 !(ext[5] & 0x02));
11898c2ecf20Sopenharmony_ci			input_report_key(wdata->extension.input,
11908c2ecf20Sopenharmony_ci				 wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
11918c2ecf20Sopenharmony_ci				 !(ext[5] & 0x01));
11928c2ecf20Sopenharmony_ci		}
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int wiimod_classic_open(struct input_dev *dev)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
12018c2ecf20Sopenharmony_ci	unsigned long flags;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
12048c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
12058c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
12068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	return 0;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic void wiimod_classic_close(struct input_dev *dev)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
12148c2ecf20Sopenharmony_ci	unsigned long flags;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
12178c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
12188c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
12198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic int wiimod_classic_probe(const struct wiimod_ops *ops,
12238c2ecf20Sopenharmony_ci				struct wiimote_data *wdata)
12248c2ecf20Sopenharmony_ci{
12258c2ecf20Sopenharmony_ci	int ret, i;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	wdata->extension.input = input_allocate_device();
12288c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
12298c2ecf20Sopenharmony_ci		return -ENOMEM;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->extension.input, wdata);
12328c2ecf20Sopenharmony_ci	wdata->extension.input->open = wiimod_classic_open;
12338c2ecf20Sopenharmony_ci	wdata->extension.input->close = wiimod_classic_close;
12348c2ecf20Sopenharmony_ci	wdata->extension.input->dev.parent = &wdata->hdev->dev;
12358c2ecf20Sopenharmony_ci	wdata->extension.input->id.bustype = wdata->hdev->bus;
12368c2ecf20Sopenharmony_ci	wdata->extension.input->id.vendor = wdata->hdev->vendor;
12378c2ecf20Sopenharmony_ci	wdata->extension.input->id.product = wdata->hdev->product;
12388c2ecf20Sopenharmony_ci	wdata->extension.input->id.version = wdata->hdev->version;
12398c2ecf20Sopenharmony_ci	wdata->extension.input->name = WIIMOTE_NAME " Classic Controller";
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->extension.input->evbit);
12428c2ecf20Sopenharmony_ci	for (i = 0; i < WIIMOD_CLASSIC_KEY_NUM; ++i)
12438c2ecf20Sopenharmony_ci		set_bit(wiimod_classic_map[i],
12448c2ecf20Sopenharmony_ci			wdata->extension.input->keybit);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->extension.input->evbit);
12478c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
12488c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1Y, wdata->extension.input->absbit);
12498c2ecf20Sopenharmony_ci	set_bit(ABS_HAT2X, wdata->extension.input->absbit);
12508c2ecf20Sopenharmony_ci	set_bit(ABS_HAT2Y, wdata->extension.input->absbit);
12518c2ecf20Sopenharmony_ci	set_bit(ABS_HAT3X, wdata->extension.input->absbit);
12528c2ecf20Sopenharmony_ci	set_bit(ABS_HAT3Y, wdata->extension.input->absbit);
12538c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
12548c2ecf20Sopenharmony_ci			     ABS_HAT1X, -30, 30, 1, 1);
12558c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
12568c2ecf20Sopenharmony_ci			     ABS_HAT1Y, -30, 30, 1, 1);
12578c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
12588c2ecf20Sopenharmony_ci			     ABS_HAT2X, -30, 30, 1, 1);
12598c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
12608c2ecf20Sopenharmony_ci			     ABS_HAT2Y, -30, 30, 1, 1);
12618c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
12628c2ecf20Sopenharmony_ci			     ABS_HAT3X, -30, 30, 1, 1);
12638c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
12648c2ecf20Sopenharmony_ci			     ABS_HAT3Y, -30, 30, 1, 1);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->extension.input);
12678c2ecf20Sopenharmony_ci	if (ret)
12688c2ecf20Sopenharmony_ci		goto err_free;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	return 0;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_cierr_free:
12738c2ecf20Sopenharmony_ci	input_free_device(wdata->extension.input);
12748c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
12758c2ecf20Sopenharmony_ci	return ret;
12768c2ecf20Sopenharmony_ci}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_cistatic void wiimod_classic_remove(const struct wiimod_ops *ops,
12798c2ecf20Sopenharmony_ci				  struct wiimote_data *wdata)
12808c2ecf20Sopenharmony_ci{
12818c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
12828c2ecf20Sopenharmony_ci		return;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	input_unregister_device(wdata->extension.input);
12858c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_classic = {
12898c2ecf20Sopenharmony_ci	.flags = 0,
12908c2ecf20Sopenharmony_ci	.arg = 0,
12918c2ecf20Sopenharmony_ci	.probe = wiimod_classic_probe,
12928c2ecf20Sopenharmony_ci	.remove = wiimod_classic_remove,
12938c2ecf20Sopenharmony_ci	.in_ext = wiimod_classic_in_ext,
12948c2ecf20Sopenharmony_ci};
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci/*
12978c2ecf20Sopenharmony_ci * Balance Board Extension
12988c2ecf20Sopenharmony_ci * The Nintendo Wii Balance Board provides four hardware weight sensor plus a
12998c2ecf20Sopenharmony_ci * single push button. No other peripherals are available. However, the
13008c2ecf20Sopenharmony_ci * balance-board data is sent via a standard Wii Remote extension. All other
13018c2ecf20Sopenharmony_ci * data for non-present hardware is zeroed out.
13028c2ecf20Sopenharmony_ci * Some 3rd party devices react allergic if we try to access normal Wii Remote
13038c2ecf20Sopenharmony_ci * hardware, so this extension module should be the only module that is loaded
13048c2ecf20Sopenharmony_ci * on balance boards.
13058c2ecf20Sopenharmony_ci * The balance board needs 8 bytes extension data instead of basic 6 bytes so
13068c2ecf20Sopenharmony_ci * it needs the WIIMOD_FLAG_EXT8 flag.
13078c2ecf20Sopenharmony_ci */
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_cistatic void wiimod_bboard_in_keys(struct wiimote_data *wdata, const __u8 *keys)
13108c2ecf20Sopenharmony_ci{
13118c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input, BTN_A,
13128c2ecf20Sopenharmony_ci			 !!(keys[1] & 0x08));
13138c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic void wiimod_bboard_in_ext(struct wiimote_data *wdata,
13178c2ecf20Sopenharmony_ci				 const __u8 *ext)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	__s32 val[4], tmp, div;
13208c2ecf20Sopenharmony_ci	unsigned int i;
13218c2ecf20Sopenharmony_ci	struct wiimote_state *s = &wdata->state;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	/*
13248c2ecf20Sopenharmony_ci	 * Balance board data layout:
13258c2ecf20Sopenharmony_ci	 *
13268c2ecf20Sopenharmony_ci	 *   Byte |  8  7  6  5  4  3  2  1  |
13278c2ecf20Sopenharmony_ci	 *   -----+--------------------------+
13288c2ecf20Sopenharmony_ci	 *    1   |    Top Right <15:8>      |
13298c2ecf20Sopenharmony_ci	 *    2   |    Top Right  <7:0>      |
13308c2ecf20Sopenharmony_ci	 *   -----+--------------------------+
13318c2ecf20Sopenharmony_ci	 *    3   | Bottom Right <15:8>      |
13328c2ecf20Sopenharmony_ci	 *    4   | Bottom Right  <7:0>      |
13338c2ecf20Sopenharmony_ci	 *   -----+--------------------------+
13348c2ecf20Sopenharmony_ci	 *    5   |     Top Left <15:8>      |
13358c2ecf20Sopenharmony_ci	 *    6   |     Top Left  <7:0>      |
13368c2ecf20Sopenharmony_ci	 *   -----+--------------------------+
13378c2ecf20Sopenharmony_ci	 *    7   |  Bottom Left <15:8>      |
13388c2ecf20Sopenharmony_ci	 *    8   |  Bottom Left  <7:0>      |
13398c2ecf20Sopenharmony_ci	 *   -----+--------------------------+
13408c2ecf20Sopenharmony_ci	 *
13418c2ecf20Sopenharmony_ci	 * These values represent the weight-measurements of the Wii-balance
13428c2ecf20Sopenharmony_ci	 * board with 16bit precision.
13438c2ecf20Sopenharmony_ci	 *
13448c2ecf20Sopenharmony_ci	 * The balance-board is never reported interleaved with motionp.
13458c2ecf20Sopenharmony_ci	 */
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	val[0] = ext[0];
13488c2ecf20Sopenharmony_ci	val[0] <<= 8;
13498c2ecf20Sopenharmony_ci	val[0] |= ext[1];
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	val[1] = ext[2];
13528c2ecf20Sopenharmony_ci	val[1] <<= 8;
13538c2ecf20Sopenharmony_ci	val[1] |= ext[3];
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	val[2] = ext[4];
13568c2ecf20Sopenharmony_ci	val[2] <<= 8;
13578c2ecf20Sopenharmony_ci	val[2] |= ext[5];
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	val[3] = ext[6];
13608c2ecf20Sopenharmony_ci	val[3] <<= 8;
13618c2ecf20Sopenharmony_ci	val[3] |= ext[7];
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	/* apply calibration data */
13648c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
13658c2ecf20Sopenharmony_ci		if (val[i] <= s->calib_bboard[i][0]) {
13668c2ecf20Sopenharmony_ci			tmp = 0;
13678c2ecf20Sopenharmony_ci		} else if (val[i] < s->calib_bboard[i][1]) {
13688c2ecf20Sopenharmony_ci			tmp = val[i] - s->calib_bboard[i][0];
13698c2ecf20Sopenharmony_ci			tmp *= 1700;
13708c2ecf20Sopenharmony_ci			div = s->calib_bboard[i][1] - s->calib_bboard[i][0];
13718c2ecf20Sopenharmony_ci			tmp /= div ? div : 1;
13728c2ecf20Sopenharmony_ci		} else {
13738c2ecf20Sopenharmony_ci			tmp = val[i] - s->calib_bboard[i][1];
13748c2ecf20Sopenharmony_ci			tmp *= 1700;
13758c2ecf20Sopenharmony_ci			div = s->calib_bboard[i][2] - s->calib_bboard[i][1];
13768c2ecf20Sopenharmony_ci			tmp /= div ? div : 1;
13778c2ecf20Sopenharmony_ci			tmp += 1700;
13788c2ecf20Sopenharmony_ci		}
13798c2ecf20Sopenharmony_ci		val[i] = tmp;
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT0X, val[0]);
13838c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT0Y, val[1]);
13848c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT1X, val[2]);
13858c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT1Y, val[3]);
13868c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic int wiimod_bboard_open(struct input_dev *dev)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
13928c2ecf20Sopenharmony_ci	unsigned long flags;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
13958c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
13968c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
13978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	return 0;
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_cistatic void wiimod_bboard_close(struct input_dev *dev)
14038c2ecf20Sopenharmony_ci{
14048c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
14058c2ecf20Sopenharmony_ci	unsigned long flags;
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
14088c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
14098c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
14108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic ssize_t wiimod_bboard_calib_show(struct device *dev,
14148c2ecf20Sopenharmony_ci					struct device_attribute *attr,
14158c2ecf20Sopenharmony_ci					char *out)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
14188c2ecf20Sopenharmony_ci	int i, j, ret;
14198c2ecf20Sopenharmony_ci	__u16 val;
14208c2ecf20Sopenharmony_ci	__u8 buf[24], offs;
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	ret = wiimote_cmd_acquire(wdata);
14238c2ecf20Sopenharmony_ci	if (ret)
14248c2ecf20Sopenharmony_ci		return ret;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa40024, buf, 12);
14278c2ecf20Sopenharmony_ci	if (ret != 12) {
14288c2ecf20Sopenharmony_ci		wiimote_cmd_release(wdata);
14298c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
14308c2ecf20Sopenharmony_ci	}
14318c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa40024 + 12, buf + 12, 12);
14328c2ecf20Sopenharmony_ci	if (ret != 12) {
14338c2ecf20Sopenharmony_ci		wiimote_cmd_release(wdata);
14348c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
14358c2ecf20Sopenharmony_ci	}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
14408c2ecf20Sopenharmony_ci	offs = 0;
14418c2ecf20Sopenharmony_ci	for (i = 0; i < 3; ++i) {
14428c2ecf20Sopenharmony_ci		for (j = 0; j < 4; ++j) {
14438c2ecf20Sopenharmony_ci			wdata->state.calib_bboard[j][i] = buf[offs];
14448c2ecf20Sopenharmony_ci			wdata->state.calib_bboard[j][i] <<= 8;
14458c2ecf20Sopenharmony_ci			wdata->state.calib_bboard[j][i] |= buf[offs + 1];
14468c2ecf20Sopenharmony_ci			offs += 2;
14478c2ecf20Sopenharmony_ci		}
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	ret = 0;
14528c2ecf20Sopenharmony_ci	for (i = 0; i < 3; ++i) {
14538c2ecf20Sopenharmony_ci		for (j = 0; j < 4; ++j) {
14548c2ecf20Sopenharmony_ci			val = wdata->state.calib_bboard[j][i];
14558c2ecf20Sopenharmony_ci			if (i == 2 && j == 3)
14568c2ecf20Sopenharmony_ci				ret += sprintf(&out[ret], "%04x\n", val);
14578c2ecf20Sopenharmony_ci			else
14588c2ecf20Sopenharmony_ci				ret += sprintf(&out[ret], "%04x:", val);
14598c2ecf20Sopenharmony_ci		}
14608c2ecf20Sopenharmony_ci	}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	return ret;
14638c2ecf20Sopenharmony_ci}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_cistatic DEVICE_ATTR(bboard_calib, S_IRUGO, wiimod_bboard_calib_show, NULL);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_cistatic int wiimod_bboard_probe(const struct wiimod_ops *ops,
14688c2ecf20Sopenharmony_ci			       struct wiimote_data *wdata)
14698c2ecf20Sopenharmony_ci{
14708c2ecf20Sopenharmony_ci	int ret, i, j;
14718c2ecf20Sopenharmony_ci	__u8 buf[24], offs;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	wiimote_cmd_acquire_noint(wdata);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa40024, buf, 12);
14768c2ecf20Sopenharmony_ci	if (ret != 12) {
14778c2ecf20Sopenharmony_ci		wiimote_cmd_release(wdata);
14788c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
14798c2ecf20Sopenharmony_ci	}
14808c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa40024 + 12, buf + 12, 12);
14818c2ecf20Sopenharmony_ci	if (ret != 12) {
14828c2ecf20Sopenharmony_ci		wiimote_cmd_release(wdata);
14838c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	offs = 0;
14898c2ecf20Sopenharmony_ci	for (i = 0; i < 3; ++i) {
14908c2ecf20Sopenharmony_ci		for (j = 0; j < 4; ++j) {
14918c2ecf20Sopenharmony_ci			wdata->state.calib_bboard[j][i] = buf[offs];
14928c2ecf20Sopenharmony_ci			wdata->state.calib_bboard[j][i] <<= 8;
14938c2ecf20Sopenharmony_ci			wdata->state.calib_bboard[j][i] |= buf[offs + 1];
14948c2ecf20Sopenharmony_ci			offs += 2;
14958c2ecf20Sopenharmony_ci		}
14968c2ecf20Sopenharmony_ci	}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	wdata->extension.input = input_allocate_device();
14998c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
15008c2ecf20Sopenharmony_ci		return -ENOMEM;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	ret = device_create_file(&wdata->hdev->dev,
15038c2ecf20Sopenharmony_ci				 &dev_attr_bboard_calib);
15048c2ecf20Sopenharmony_ci	if (ret) {
15058c2ecf20Sopenharmony_ci		hid_err(wdata->hdev, "cannot create sysfs attribute\n");
15068c2ecf20Sopenharmony_ci		goto err_free;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->extension.input, wdata);
15108c2ecf20Sopenharmony_ci	wdata->extension.input->open = wiimod_bboard_open;
15118c2ecf20Sopenharmony_ci	wdata->extension.input->close = wiimod_bboard_close;
15128c2ecf20Sopenharmony_ci	wdata->extension.input->dev.parent = &wdata->hdev->dev;
15138c2ecf20Sopenharmony_ci	wdata->extension.input->id.bustype = wdata->hdev->bus;
15148c2ecf20Sopenharmony_ci	wdata->extension.input->id.vendor = wdata->hdev->vendor;
15158c2ecf20Sopenharmony_ci	wdata->extension.input->id.product = wdata->hdev->product;
15168c2ecf20Sopenharmony_ci	wdata->extension.input->id.version = wdata->hdev->version;
15178c2ecf20Sopenharmony_ci	wdata->extension.input->name = WIIMOTE_NAME " Balance Board";
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->extension.input->evbit);
15208c2ecf20Sopenharmony_ci	set_bit(BTN_A, wdata->extension.input->keybit);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->extension.input->evbit);
15238c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
15248c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
15258c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
15268c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1Y, wdata->extension.input->absbit);
15278c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
15288c2ecf20Sopenharmony_ci			     ABS_HAT0X, 0, 65535, 2, 4);
15298c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
15308c2ecf20Sopenharmony_ci			     ABS_HAT0Y, 0, 65535, 2, 4);
15318c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
15328c2ecf20Sopenharmony_ci			     ABS_HAT1X, 0, 65535, 2, 4);
15338c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
15348c2ecf20Sopenharmony_ci			     ABS_HAT1Y, 0, 65535, 2, 4);
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->extension.input);
15378c2ecf20Sopenharmony_ci	if (ret)
15388c2ecf20Sopenharmony_ci		goto err_file;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	return 0;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cierr_file:
15438c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev,
15448c2ecf20Sopenharmony_ci			   &dev_attr_bboard_calib);
15458c2ecf20Sopenharmony_cierr_free:
15468c2ecf20Sopenharmony_ci	input_free_device(wdata->extension.input);
15478c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
15488c2ecf20Sopenharmony_ci	return ret;
15498c2ecf20Sopenharmony_ci}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_cistatic void wiimod_bboard_remove(const struct wiimod_ops *ops,
15528c2ecf20Sopenharmony_ci				 struct wiimote_data *wdata)
15538c2ecf20Sopenharmony_ci{
15548c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
15558c2ecf20Sopenharmony_ci		return;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	input_unregister_device(wdata->extension.input);
15588c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
15598c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev,
15608c2ecf20Sopenharmony_ci			   &dev_attr_bboard_calib);
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_bboard = {
15648c2ecf20Sopenharmony_ci	.flags = WIIMOD_FLAG_EXT8,
15658c2ecf20Sopenharmony_ci	.arg = 0,
15668c2ecf20Sopenharmony_ci	.probe = wiimod_bboard_probe,
15678c2ecf20Sopenharmony_ci	.remove = wiimod_bboard_remove,
15688c2ecf20Sopenharmony_ci	.in_keys = wiimod_bboard_in_keys,
15698c2ecf20Sopenharmony_ci	.in_ext = wiimod_bboard_in_ext,
15708c2ecf20Sopenharmony_ci};
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci/*
15738c2ecf20Sopenharmony_ci * Pro Controller
15748c2ecf20Sopenharmony_ci * Released with the Wii U was the Nintendo Wii U Pro Controller. It does not
15758c2ecf20Sopenharmony_ci * work together with the classic Wii, but only with the new Wii U. However, it
15768c2ecf20Sopenharmony_ci * uses the same protocol and provides a builtin "classic controller pro"
15778c2ecf20Sopenharmony_ci * extension, few standard buttons, a rumble motor, 4 LEDs and a battery.
15788c2ecf20Sopenharmony_ci * We provide all these via a standard extension device as the device doesn't
15798c2ecf20Sopenharmony_ci * feature an extension port.
15808c2ecf20Sopenharmony_ci */
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cienum wiimod_pro_keys {
15838c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_A,
15848c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_B,
15858c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_X,
15868c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_Y,
15878c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_PLUS,
15888c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_MINUS,
15898c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_HOME,
15908c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_LEFT,
15918c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_RIGHT,
15928c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_UP,
15938c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_DOWN,
15948c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_TL,
15958c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_TR,
15968c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_ZL,
15978c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_ZR,
15988c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_THUMBL,
15998c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_THUMBR,
16008c2ecf20Sopenharmony_ci	WIIMOD_PRO_KEY_NUM,
16018c2ecf20Sopenharmony_ci};
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_cistatic const __u16 wiimod_pro_map[] = {
16048c2ecf20Sopenharmony_ci	BTN_EAST,	/* WIIMOD_PRO_KEY_A */
16058c2ecf20Sopenharmony_ci	BTN_SOUTH,	/* WIIMOD_PRO_KEY_B */
16068c2ecf20Sopenharmony_ci	BTN_NORTH,	/* WIIMOD_PRO_KEY_X */
16078c2ecf20Sopenharmony_ci	BTN_WEST,	/* WIIMOD_PRO_KEY_Y */
16088c2ecf20Sopenharmony_ci	BTN_START,	/* WIIMOD_PRO_KEY_PLUS */
16098c2ecf20Sopenharmony_ci	BTN_SELECT,	/* WIIMOD_PRO_KEY_MINUS */
16108c2ecf20Sopenharmony_ci	BTN_MODE,	/* WIIMOD_PRO_KEY_HOME */
16118c2ecf20Sopenharmony_ci	BTN_DPAD_LEFT,	/* WIIMOD_PRO_KEY_LEFT */
16128c2ecf20Sopenharmony_ci	BTN_DPAD_RIGHT,	/* WIIMOD_PRO_KEY_RIGHT */
16138c2ecf20Sopenharmony_ci	BTN_DPAD_UP,	/* WIIMOD_PRO_KEY_UP */
16148c2ecf20Sopenharmony_ci	BTN_DPAD_DOWN,	/* WIIMOD_PRO_KEY_DOWN */
16158c2ecf20Sopenharmony_ci	BTN_TL,		/* WIIMOD_PRO_KEY_TL */
16168c2ecf20Sopenharmony_ci	BTN_TR,		/* WIIMOD_PRO_KEY_TR */
16178c2ecf20Sopenharmony_ci	BTN_TL2,	/* WIIMOD_PRO_KEY_ZL */
16188c2ecf20Sopenharmony_ci	BTN_TR2,	/* WIIMOD_PRO_KEY_ZR */
16198c2ecf20Sopenharmony_ci	BTN_THUMBL,	/* WIIMOD_PRO_KEY_THUMBL */
16208c2ecf20Sopenharmony_ci	BTN_THUMBR,	/* WIIMOD_PRO_KEY_THUMBR */
16218c2ecf20Sopenharmony_ci};
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_cistatic void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext)
16248c2ecf20Sopenharmony_ci{
16258c2ecf20Sopenharmony_ci	__s16 rx, ry, lx, ly;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
16288c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
16298c2ecf20Sopenharmony_ci	 *    1   |                   LX <7:0>                    |
16308c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16318c2ecf20Sopenharmony_ci	 *    2   |  0     0     0     0  |       LX <11:8>       |
16328c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16338c2ecf20Sopenharmony_ci	 *    3   |                   RX <7:0>                    |
16348c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16358c2ecf20Sopenharmony_ci	 *    4   |  0     0     0     0  |       RX <11:8>       |
16368c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16378c2ecf20Sopenharmony_ci	 *    5   |                   LY <7:0>                    |
16388c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16398c2ecf20Sopenharmony_ci	 *    6   |  0     0     0     0  |       LY <11:8>       |
16408c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16418c2ecf20Sopenharmony_ci	 *    7   |                   RY <7:0>                    |
16428c2ecf20Sopenharmony_ci	 *   -----+-----------------------+-----------------------+
16438c2ecf20Sopenharmony_ci	 *    8   |  0     0     0     0  |       RY <11:8>       |
16448c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
16458c2ecf20Sopenharmony_ci	 *    9   | BDR | BDD | BLT | B-  | BH  | B+  | BRT |  1  |
16468c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
16478c2ecf20Sopenharmony_ci	 *   10   | BZL | BB  | BY  | BA  | BX  | BZR | BDL | BDU |
16488c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
16498c2ecf20Sopenharmony_ci	 *   11   |  1  |     BATTERY     | USB |CHARG|LTHUM|RTHUM|
16508c2ecf20Sopenharmony_ci	 *   -----+-----+-----------------+-----------+-----+-----+
16518c2ecf20Sopenharmony_ci	 * All buttons are low-active (0 if pressed)
16528c2ecf20Sopenharmony_ci	 * RX and RY are right analog stick
16538c2ecf20Sopenharmony_ci	 * LX and LY are left analog stick
16548c2ecf20Sopenharmony_ci	 * BLT is left trigger, BRT is right trigger.
16558c2ecf20Sopenharmony_ci	 * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
16568c2ecf20Sopenharmony_ci	 * BZL is left Z button and BZR is right Z button
16578c2ecf20Sopenharmony_ci	 * B-, BH, B+ are +, HOME and - buttons
16588c2ecf20Sopenharmony_ci	 * BB, BY, BA, BX are A, B, X, Y buttons
16598c2ecf20Sopenharmony_ci	 *
16608c2ecf20Sopenharmony_ci	 * Bits marked as 0/1 are unknown and never changed during tests.
16618c2ecf20Sopenharmony_ci	 *
16628c2ecf20Sopenharmony_ci	 * Not entirely verified:
16638c2ecf20Sopenharmony_ci	 *   CHARG: 1 if uncharging, 0 if charging
16648c2ecf20Sopenharmony_ci	 *   USB: 1 if not connected, 0 if connected
16658c2ecf20Sopenharmony_ci	 *   BATTERY: battery capacity from 000 (empty) to 100 (full)
16668c2ecf20Sopenharmony_ci	 */
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	lx = (ext[0] & 0xff) | ((ext[1] & 0x0f) << 8);
16698c2ecf20Sopenharmony_ci	rx = (ext[2] & 0xff) | ((ext[3] & 0x0f) << 8);
16708c2ecf20Sopenharmony_ci	ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8);
16718c2ecf20Sopenharmony_ci	ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8);
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	/* zero-point offsets */
16748c2ecf20Sopenharmony_ci	lx -= 0x800;
16758c2ecf20Sopenharmony_ci	ly = 0x800 - ly;
16768c2ecf20Sopenharmony_ci	rx -= 0x800;
16778c2ecf20Sopenharmony_ci	ry = 0x800 - ry;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	/* Trivial automatic calibration. We don't know any calibration data
16808c2ecf20Sopenharmony_ci	 * in the EEPROM so we must use the first report to calibrate the
16818c2ecf20Sopenharmony_ci	 * null-position of the analog sticks. Users can retrigger calibration
16828c2ecf20Sopenharmony_ci	 * via sysfs, or set it explicitly. If data is off more than abs(500),
16838c2ecf20Sopenharmony_ci	 * we skip calibration as the sticks are likely to be moved already. */
16848c2ecf20Sopenharmony_ci	if (!(wdata->state.flags & WIIPROTO_FLAG_PRO_CALIB_DONE)) {
16858c2ecf20Sopenharmony_ci		wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE;
16868c2ecf20Sopenharmony_ci		if (abs(lx) < 500)
16878c2ecf20Sopenharmony_ci			wdata->state.calib_pro_sticks[0] = -lx;
16888c2ecf20Sopenharmony_ci		if (abs(ly) < 500)
16898c2ecf20Sopenharmony_ci			wdata->state.calib_pro_sticks[1] = -ly;
16908c2ecf20Sopenharmony_ci		if (abs(rx) < 500)
16918c2ecf20Sopenharmony_ci			wdata->state.calib_pro_sticks[2] = -rx;
16928c2ecf20Sopenharmony_ci		if (abs(ry) < 500)
16938c2ecf20Sopenharmony_ci			wdata->state.calib_pro_sticks[3] = -ry;
16948c2ecf20Sopenharmony_ci	}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	/* apply calibration data */
16978c2ecf20Sopenharmony_ci	lx += wdata->state.calib_pro_sticks[0];
16988c2ecf20Sopenharmony_ci	ly += wdata->state.calib_pro_sticks[1];
16998c2ecf20Sopenharmony_ci	rx += wdata->state.calib_pro_sticks[2];
17008c2ecf20Sopenharmony_ci	ry += wdata->state.calib_pro_sticks[3];
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_X, lx);
17038c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_Y, ly);
17048c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_RX, rx);
17058c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_RY, ry);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17088c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT],
17098c2ecf20Sopenharmony_ci			 !(ext[8] & 0x80));
17108c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17118c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_DOWN],
17128c2ecf20Sopenharmony_ci			 !(ext[8] & 0x40));
17138c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17148c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_TL],
17158c2ecf20Sopenharmony_ci			 !(ext[8] & 0x20));
17168c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17178c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_MINUS],
17188c2ecf20Sopenharmony_ci			 !(ext[8] & 0x10));
17198c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17208c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_HOME],
17218c2ecf20Sopenharmony_ci			 !(ext[8] & 0x08));
17228c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17238c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_PLUS],
17248c2ecf20Sopenharmony_ci			 !(ext[8] & 0x04));
17258c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17268c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_TR],
17278c2ecf20Sopenharmony_ci			 !(ext[8] & 0x02));
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17308c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_ZL],
17318c2ecf20Sopenharmony_ci			 !(ext[9] & 0x80));
17328c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17338c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_B],
17348c2ecf20Sopenharmony_ci			 !(ext[9] & 0x40));
17358c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17368c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_Y],
17378c2ecf20Sopenharmony_ci			 !(ext[9] & 0x20));
17388c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17398c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_A],
17408c2ecf20Sopenharmony_ci			 !(ext[9] & 0x10));
17418c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17428c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_X],
17438c2ecf20Sopenharmony_ci			 !(ext[9] & 0x08));
17448c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17458c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_ZR],
17468c2ecf20Sopenharmony_ci			 !(ext[9] & 0x04));
17478c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17488c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_LEFT],
17498c2ecf20Sopenharmony_ci			 !(ext[9] & 0x02));
17508c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17518c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_UP],
17528c2ecf20Sopenharmony_ci			 !(ext[9] & 0x01));
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17558c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_THUMBL],
17568c2ecf20Sopenharmony_ci			 !(ext[10] & 0x02));
17578c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
17588c2ecf20Sopenharmony_ci			 wiimod_pro_map[WIIMOD_PRO_KEY_THUMBR],
17598c2ecf20Sopenharmony_ci			 !(ext[10] & 0x01));
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_cistatic int wiimod_pro_open(struct input_dev *dev)
17658c2ecf20Sopenharmony_ci{
17668c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
17678c2ecf20Sopenharmony_ci	unsigned long flags;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
17708c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
17718c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
17728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	return 0;
17758c2ecf20Sopenharmony_ci}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_cistatic void wiimod_pro_close(struct input_dev *dev)
17788c2ecf20Sopenharmony_ci{
17798c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
17808c2ecf20Sopenharmony_ci	unsigned long flags;
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
17838c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
17848c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
17858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
17868c2ecf20Sopenharmony_ci}
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_cistatic int wiimod_pro_play(struct input_dev *dev, void *data,
17898c2ecf20Sopenharmony_ci			   struct ff_effect *eff)
17908c2ecf20Sopenharmony_ci{
17918c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
17928c2ecf20Sopenharmony_ci	__u8 value;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	/*
17958c2ecf20Sopenharmony_ci	 * The wiimote supports only a single rumble motor so if any magnitude
17968c2ecf20Sopenharmony_ci	 * is set to non-zero then we start the rumble motor. If both are set to
17978c2ecf20Sopenharmony_ci	 * zero, we stop the rumble motor.
17988c2ecf20Sopenharmony_ci	 */
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
18018c2ecf20Sopenharmony_ci		value = 1;
18028c2ecf20Sopenharmony_ci	else
18038c2ecf20Sopenharmony_ci		value = 0;
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	/* Locking state.lock here might deadlock with input_event() calls.
18068c2ecf20Sopenharmony_ci	 * schedule_work acts as barrier. Merging multiple changes is fine. */
18078c2ecf20Sopenharmony_ci	wdata->state.cache_rumble = value;
18088c2ecf20Sopenharmony_ci	schedule_work(&wdata->rumble_worker);
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	return 0;
18118c2ecf20Sopenharmony_ci}
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_cistatic ssize_t wiimod_pro_calib_show(struct device *dev,
18148c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
18158c2ecf20Sopenharmony_ci				     char *out)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
18188c2ecf20Sopenharmony_ci	int r;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	r = 0;
18218c2ecf20Sopenharmony_ci	r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[0]);
18228c2ecf20Sopenharmony_ci	r += sprintf(&out[r], "%+06hd ", wdata->state.calib_pro_sticks[1]);
18238c2ecf20Sopenharmony_ci	r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[2]);
18248c2ecf20Sopenharmony_ci	r += sprintf(&out[r], "%+06hd\n", wdata->state.calib_pro_sticks[3]);
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	return r;
18278c2ecf20Sopenharmony_ci}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_cistatic ssize_t wiimod_pro_calib_store(struct device *dev,
18308c2ecf20Sopenharmony_ci				      struct device_attribute *attr,
18318c2ecf20Sopenharmony_ci				      const char *buf, size_t count)
18328c2ecf20Sopenharmony_ci{
18338c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
18348c2ecf20Sopenharmony_ci	int r;
18358c2ecf20Sopenharmony_ci	s16 x1, y1, x2, y2;
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	if (!strncmp(buf, "scan\n", 5)) {
18388c2ecf20Sopenharmony_ci		spin_lock_irq(&wdata->state.lock);
18398c2ecf20Sopenharmony_ci		wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE;
18408c2ecf20Sopenharmony_ci		spin_unlock_irq(&wdata->state.lock);
18418c2ecf20Sopenharmony_ci	} else {
18428c2ecf20Sopenharmony_ci		r = sscanf(buf, "%hd:%hd %hd:%hd", &x1, &y1, &x2, &y2);
18438c2ecf20Sopenharmony_ci		if (r != 4)
18448c2ecf20Sopenharmony_ci			return -EINVAL;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci		spin_lock_irq(&wdata->state.lock);
18478c2ecf20Sopenharmony_ci		wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE;
18488c2ecf20Sopenharmony_ci		spin_unlock_irq(&wdata->state.lock);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci		wdata->state.calib_pro_sticks[0] = x1;
18518c2ecf20Sopenharmony_ci		wdata->state.calib_pro_sticks[1] = y1;
18528c2ecf20Sopenharmony_ci		wdata->state.calib_pro_sticks[2] = x2;
18538c2ecf20Sopenharmony_ci		wdata->state.calib_pro_sticks[3] = y2;
18548c2ecf20Sopenharmony_ci	}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	return strnlen(buf, PAGE_SIZE);
18578c2ecf20Sopenharmony_ci}
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(pro_calib, S_IRUGO|S_IWUSR|S_IWGRP, wiimod_pro_calib_show,
18608c2ecf20Sopenharmony_ci		   wiimod_pro_calib_store);
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_cistatic int wiimod_pro_probe(const struct wiimod_ops *ops,
18638c2ecf20Sopenharmony_ci			    struct wiimote_data *wdata)
18648c2ecf20Sopenharmony_ci{
18658c2ecf20Sopenharmony_ci	int ret, i;
18668c2ecf20Sopenharmony_ci	unsigned long flags;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
18698c2ecf20Sopenharmony_ci	wdata->state.calib_pro_sticks[0] = 0;
18708c2ecf20Sopenharmony_ci	wdata->state.calib_pro_sticks[1] = 0;
18718c2ecf20Sopenharmony_ci	wdata->state.calib_pro_sticks[2] = 0;
18728c2ecf20Sopenharmony_ci	wdata->state.calib_pro_sticks[3] = 0;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
18758c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE;
18768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	wdata->extension.input = input_allocate_device();
18798c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
18808c2ecf20Sopenharmony_ci		return -ENOMEM;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	set_bit(FF_RUMBLE, wdata->extension.input->ffbit);
18838c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->extension.input, wdata);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	if (input_ff_create_memless(wdata->extension.input, NULL,
18868c2ecf20Sopenharmony_ci				    wiimod_pro_play)) {
18878c2ecf20Sopenharmony_ci		ret = -ENOMEM;
18888c2ecf20Sopenharmony_ci		goto err_free;
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	ret = device_create_file(&wdata->hdev->dev,
18928c2ecf20Sopenharmony_ci				 &dev_attr_pro_calib);
18938c2ecf20Sopenharmony_ci	if (ret) {
18948c2ecf20Sopenharmony_ci		hid_err(wdata->hdev, "cannot create sysfs attribute\n");
18958c2ecf20Sopenharmony_ci		goto err_free;
18968c2ecf20Sopenharmony_ci	}
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	wdata->extension.input->open = wiimod_pro_open;
18998c2ecf20Sopenharmony_ci	wdata->extension.input->close = wiimod_pro_close;
19008c2ecf20Sopenharmony_ci	wdata->extension.input->dev.parent = &wdata->hdev->dev;
19018c2ecf20Sopenharmony_ci	wdata->extension.input->id.bustype = wdata->hdev->bus;
19028c2ecf20Sopenharmony_ci	wdata->extension.input->id.vendor = wdata->hdev->vendor;
19038c2ecf20Sopenharmony_ci	wdata->extension.input->id.product = wdata->hdev->product;
19048c2ecf20Sopenharmony_ci	wdata->extension.input->id.version = wdata->hdev->version;
19058c2ecf20Sopenharmony_ci	wdata->extension.input->name = WIIMOTE_NAME " Pro Controller";
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->extension.input->evbit);
19088c2ecf20Sopenharmony_ci	for (i = 0; i < WIIMOD_PRO_KEY_NUM; ++i)
19098c2ecf20Sopenharmony_ci		set_bit(wiimod_pro_map[i],
19108c2ecf20Sopenharmony_ci			wdata->extension.input->keybit);
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->extension.input->evbit);
19138c2ecf20Sopenharmony_ci	set_bit(ABS_X, wdata->extension.input->absbit);
19148c2ecf20Sopenharmony_ci	set_bit(ABS_Y, wdata->extension.input->absbit);
19158c2ecf20Sopenharmony_ci	set_bit(ABS_RX, wdata->extension.input->absbit);
19168c2ecf20Sopenharmony_ci	set_bit(ABS_RY, wdata->extension.input->absbit);
19178c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
19188c2ecf20Sopenharmony_ci			     ABS_X, -0x400, 0x400, 4, 100);
19198c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
19208c2ecf20Sopenharmony_ci			     ABS_Y, -0x400, 0x400, 4, 100);
19218c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
19228c2ecf20Sopenharmony_ci			     ABS_RX, -0x400, 0x400, 4, 100);
19238c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
19248c2ecf20Sopenharmony_ci			     ABS_RY, -0x400, 0x400, 4, 100);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->extension.input);
19278c2ecf20Sopenharmony_ci	if (ret)
19288c2ecf20Sopenharmony_ci		goto err_file;
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	return 0;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_cierr_file:
19338c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev,
19348c2ecf20Sopenharmony_ci			   &dev_attr_pro_calib);
19358c2ecf20Sopenharmony_cierr_free:
19368c2ecf20Sopenharmony_ci	input_free_device(wdata->extension.input);
19378c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
19388c2ecf20Sopenharmony_ci	return ret;
19398c2ecf20Sopenharmony_ci}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_cistatic void wiimod_pro_remove(const struct wiimod_ops *ops,
19428c2ecf20Sopenharmony_ci			      struct wiimote_data *wdata)
19438c2ecf20Sopenharmony_ci{
19448c2ecf20Sopenharmony_ci	unsigned long flags;
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
19478c2ecf20Sopenharmony_ci		return;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	input_unregister_device(wdata->extension.input);
19508c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
19518c2ecf20Sopenharmony_ci	cancel_work_sync(&wdata->rumble_worker);
19528c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev,
19538c2ecf20Sopenharmony_ci			   &dev_attr_pro_calib);
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
19568c2ecf20Sopenharmony_ci	wiiproto_req_rumble(wdata, 0);
19578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_pro = {
19618c2ecf20Sopenharmony_ci	.flags = WIIMOD_FLAG_EXT16,
19628c2ecf20Sopenharmony_ci	.arg = 0,
19638c2ecf20Sopenharmony_ci	.probe = wiimod_pro_probe,
19648c2ecf20Sopenharmony_ci	.remove = wiimod_pro_remove,
19658c2ecf20Sopenharmony_ci	.in_ext = wiimod_pro_in_ext,
19668c2ecf20Sopenharmony_ci};
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci/*
19698c2ecf20Sopenharmony_ci * Drums
19708c2ecf20Sopenharmony_ci * Guitar-Hero, Rock-Band and other games came bundled with drums which can
19718c2ecf20Sopenharmony_ci * be plugged as extension to a Wiimote. Drum-reports are still not entirely
19728c2ecf20Sopenharmony_ci * figured out, but the most important information is known.
19738c2ecf20Sopenharmony_ci * We create a separate device for drums and report all information via this
19748c2ecf20Sopenharmony_ci * input device.
19758c2ecf20Sopenharmony_ci */
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_cistatic inline void wiimod_drums_report_pressure(struct wiimote_data *wdata,
19788c2ecf20Sopenharmony_ci						__u8 none, __u8 which,
19798c2ecf20Sopenharmony_ci						__u8 pressure, __u8 onoff,
19808c2ecf20Sopenharmony_ci						__u8 *store, __u16 code,
19818c2ecf20Sopenharmony_ci						__u8 which_code)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	static const __u8 default_pressure = 3;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	if (!none && which == which_code) {
19868c2ecf20Sopenharmony_ci		*store = pressure;
19878c2ecf20Sopenharmony_ci		input_report_abs(wdata->extension.input, code, *store);
19888c2ecf20Sopenharmony_ci	} else if (onoff != !!*store) {
19898c2ecf20Sopenharmony_ci		*store = onoff ? default_pressure : 0;
19908c2ecf20Sopenharmony_ci		input_report_abs(wdata->extension.input, code, *store);
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci}
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_cistatic void wiimod_drums_in_ext(struct wiimote_data *wdata, const __u8 *ext)
19958c2ecf20Sopenharmony_ci{
19968c2ecf20Sopenharmony_ci	__u8 pressure, which, none, hhp, sx, sy;
19978c2ecf20Sopenharmony_ci	__u8 o, r, y, g, b, bass, bm, bp;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
20008c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20018c2ecf20Sopenharmony_ci	 *    1   |  0  |  0  |              SX <5:0>             |
20028c2ecf20Sopenharmony_ci	 *    2   |  0  |  0  |              SY <5:0>             |
20038c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----------------------------+-----+
20048c2ecf20Sopenharmony_ci	 *    3   | HPP | NON |         WHICH <5:1>         |  ?  |
20058c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20068c2ecf20Sopenharmony_ci	 *    4   |   SOFT <7:5>    |  0  |  1  |  1  |  0  |  ?  |
20078c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20088c2ecf20Sopenharmony_ci	 *    5   |  ?  |  1  |  1  | B-  |  1  | B+  |  1  |  ?  |
20098c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20108c2ecf20Sopenharmony_ci	 *    6   |  O  |  R  |  Y  |  G  |  B  | BSS |  1  |  1  |
20118c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20128c2ecf20Sopenharmony_ci	 * All buttons are 0 if pressed
20138c2ecf20Sopenharmony_ci	 *
20148c2ecf20Sopenharmony_ci	 * With Motion+ enabled, the following bits will get invalid:
20158c2ecf20Sopenharmony_ci	 *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
20168c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20178c2ecf20Sopenharmony_ci	 *    1   |  0  |  0  |              SX <5:1>       |XXXXX|
20188c2ecf20Sopenharmony_ci	 *    2   |  0  |  0  |              SY <5:1>       |XXXXX|
20198c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----------------------------+-----+
20208c2ecf20Sopenharmony_ci	 *    3   | HPP | NON |         WHICH <5:1>         |  ?  |
20218c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20228c2ecf20Sopenharmony_ci	 *    4   |   SOFT <7:5>    |  0  |  1  |  1  |  0  |  ?  |
20238c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20248c2ecf20Sopenharmony_ci	 *    5   |  ?  |  1  |  1  | B-  |  1  | B+  |  1  |XXXXX|
20258c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20268c2ecf20Sopenharmony_ci	 *    6   |  O  |  R  |  Y  |  G  |  B  | BSS |XXXXX|XXXXX|
20278c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
20288c2ecf20Sopenharmony_ci	 */
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	pressure = 7 - (ext[3] >> 5);
20318c2ecf20Sopenharmony_ci	which = (ext[2] >> 1) & 0x1f;
20328c2ecf20Sopenharmony_ci	none = !!(ext[2] & 0x40);
20338c2ecf20Sopenharmony_ci	hhp = !(ext[2] & 0x80);
20348c2ecf20Sopenharmony_ci	sx = ext[0] & 0x3f;
20358c2ecf20Sopenharmony_ci	sy = ext[1] & 0x3f;
20368c2ecf20Sopenharmony_ci	o = !(ext[5] & 0x80);
20378c2ecf20Sopenharmony_ci	r = !(ext[5] & 0x40);
20388c2ecf20Sopenharmony_ci	y = !(ext[5] & 0x20);
20398c2ecf20Sopenharmony_ci	g = !(ext[5] & 0x10);
20408c2ecf20Sopenharmony_ci	b = !(ext[5] & 0x08);
20418c2ecf20Sopenharmony_ci	bass = !(ext[5] & 0x04);
20428c2ecf20Sopenharmony_ci	bm = !(ext[4] & 0x10);
20438c2ecf20Sopenharmony_ci	bp = !(ext[4] & 0x04);
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
20468c2ecf20Sopenharmony_ci		sx &= 0x3e;
20478c2ecf20Sopenharmony_ci		sy &= 0x3e;
20488c2ecf20Sopenharmony_ci	}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, which, pressure,
20518c2ecf20Sopenharmony_ci				     o, &wdata->state.pressure_drums[0],
20528c2ecf20Sopenharmony_ci				     ABS_HAT2Y, 0x0e);
20538c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, which, pressure,
20548c2ecf20Sopenharmony_ci				     r, &wdata->state.pressure_drums[1],
20558c2ecf20Sopenharmony_ci				     ABS_HAT0X, 0x19);
20568c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, which, pressure,
20578c2ecf20Sopenharmony_ci				     y, &wdata->state.pressure_drums[2],
20588c2ecf20Sopenharmony_ci				     ABS_HAT2X, 0x11);
20598c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, which, pressure,
20608c2ecf20Sopenharmony_ci				     g, &wdata->state.pressure_drums[3],
20618c2ecf20Sopenharmony_ci				     ABS_HAT1X, 0x12);
20628c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, which, pressure,
20638c2ecf20Sopenharmony_ci				     b, &wdata->state.pressure_drums[4],
20648c2ecf20Sopenharmony_ci				     ABS_HAT0Y, 0x0f);
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	/* Bass shares pressure with hi-hat (set via hhp) */
20678c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, hhp ? 0xff : which, pressure,
20688c2ecf20Sopenharmony_ci				     bass, &wdata->state.pressure_drums[5],
20698c2ecf20Sopenharmony_ci				     ABS_HAT3X, 0x1b);
20708c2ecf20Sopenharmony_ci	/* Hi-hat has no on/off values, just pressure. Force to off/0. */
20718c2ecf20Sopenharmony_ci	wiimod_drums_report_pressure(wdata, none, hhp ? which : 0xff, pressure,
20728c2ecf20Sopenharmony_ci				     0, &wdata->state.pressure_drums[6],
20738c2ecf20Sopenharmony_ci				     ABS_HAT3Y, 0x0e);
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_X, sx - 0x20);
20768c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20);
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input, BTN_START, bp);
20798c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input, BTN_SELECT, bm);
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
20828c2ecf20Sopenharmony_ci}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_cistatic int wiimod_drums_open(struct input_dev *dev)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
20878c2ecf20Sopenharmony_ci	unsigned long flags;
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
20908c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
20918c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
20928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	return 0;
20958c2ecf20Sopenharmony_ci}
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_cistatic void wiimod_drums_close(struct input_dev *dev)
20988c2ecf20Sopenharmony_ci{
20998c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
21008c2ecf20Sopenharmony_ci	unsigned long flags;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
21038c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
21048c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
21058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
21068c2ecf20Sopenharmony_ci}
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_cistatic int wiimod_drums_probe(const struct wiimod_ops *ops,
21098c2ecf20Sopenharmony_ci			      struct wiimote_data *wdata)
21108c2ecf20Sopenharmony_ci{
21118c2ecf20Sopenharmony_ci	int ret;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	wdata->extension.input = input_allocate_device();
21148c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
21158c2ecf20Sopenharmony_ci		return -ENOMEM;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->extension.input, wdata);
21188c2ecf20Sopenharmony_ci	wdata->extension.input->open = wiimod_drums_open;
21198c2ecf20Sopenharmony_ci	wdata->extension.input->close = wiimod_drums_close;
21208c2ecf20Sopenharmony_ci	wdata->extension.input->dev.parent = &wdata->hdev->dev;
21218c2ecf20Sopenharmony_ci	wdata->extension.input->id.bustype = wdata->hdev->bus;
21228c2ecf20Sopenharmony_ci	wdata->extension.input->id.vendor = wdata->hdev->vendor;
21238c2ecf20Sopenharmony_ci	wdata->extension.input->id.product = wdata->hdev->product;
21248c2ecf20Sopenharmony_ci	wdata->extension.input->id.version = wdata->hdev->version;
21258c2ecf20Sopenharmony_ci	wdata->extension.input->name = WIIMOTE_NAME " Drums";
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->extension.input->evbit);
21288c2ecf20Sopenharmony_ci	set_bit(BTN_START, wdata->extension.input->keybit);
21298c2ecf20Sopenharmony_ci	set_bit(BTN_SELECT, wdata->extension.input->keybit);
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->extension.input->evbit);
21328c2ecf20Sopenharmony_ci	set_bit(ABS_X, wdata->extension.input->absbit);
21338c2ecf20Sopenharmony_ci	set_bit(ABS_Y, wdata->extension.input->absbit);
21348c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
21358c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
21368c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
21378c2ecf20Sopenharmony_ci	set_bit(ABS_HAT2X, wdata->extension.input->absbit);
21388c2ecf20Sopenharmony_ci	set_bit(ABS_HAT2Y, wdata->extension.input->absbit);
21398c2ecf20Sopenharmony_ci	set_bit(ABS_HAT3X, wdata->extension.input->absbit);
21408c2ecf20Sopenharmony_ci	set_bit(ABS_HAT3Y, wdata->extension.input->absbit);
21418c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21428c2ecf20Sopenharmony_ci			     ABS_X, -32, 31, 1, 1);
21438c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21448c2ecf20Sopenharmony_ci			     ABS_Y, -32, 31, 1, 1);
21458c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21468c2ecf20Sopenharmony_ci			     ABS_HAT0X, 0, 7, 0, 0);
21478c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21488c2ecf20Sopenharmony_ci			     ABS_HAT0Y, 0, 7, 0, 0);
21498c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21508c2ecf20Sopenharmony_ci			     ABS_HAT1X, 0, 7, 0, 0);
21518c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21528c2ecf20Sopenharmony_ci			     ABS_HAT2X, 0, 7, 0, 0);
21538c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21548c2ecf20Sopenharmony_ci			     ABS_HAT2Y, 0, 7, 0, 0);
21558c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21568c2ecf20Sopenharmony_ci			     ABS_HAT3X, 0, 7, 0, 0);
21578c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
21588c2ecf20Sopenharmony_ci			     ABS_HAT3Y, 0, 7, 0, 0);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->extension.input);
21618c2ecf20Sopenharmony_ci	if (ret)
21628c2ecf20Sopenharmony_ci		goto err_free;
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	return 0;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_cierr_free:
21678c2ecf20Sopenharmony_ci	input_free_device(wdata->extension.input);
21688c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
21698c2ecf20Sopenharmony_ci	return ret;
21708c2ecf20Sopenharmony_ci}
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_cistatic void wiimod_drums_remove(const struct wiimod_ops *ops,
21738c2ecf20Sopenharmony_ci				struct wiimote_data *wdata)
21748c2ecf20Sopenharmony_ci{
21758c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
21768c2ecf20Sopenharmony_ci		return;
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	input_unregister_device(wdata->extension.input);
21798c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
21808c2ecf20Sopenharmony_ci}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_drums = {
21838c2ecf20Sopenharmony_ci	.flags = 0,
21848c2ecf20Sopenharmony_ci	.arg = 0,
21858c2ecf20Sopenharmony_ci	.probe = wiimod_drums_probe,
21868c2ecf20Sopenharmony_ci	.remove = wiimod_drums_remove,
21878c2ecf20Sopenharmony_ci	.in_ext = wiimod_drums_in_ext,
21888c2ecf20Sopenharmony_ci};
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci/*
21918c2ecf20Sopenharmony_ci * Guitar
21928c2ecf20Sopenharmony_ci * Guitar-Hero, Rock-Band and other games came bundled with guitars which can
21938c2ecf20Sopenharmony_ci * be plugged as extension to a Wiimote.
21948c2ecf20Sopenharmony_ci * We create a separate device for guitars and report all information via this
21958c2ecf20Sopenharmony_ci * input device.
21968c2ecf20Sopenharmony_ci */
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_cienum wiimod_guitar_keys {
21998c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_G,
22008c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_R,
22018c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_Y,
22028c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_B,
22038c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_O,
22048c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_UP,
22058c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_DOWN,
22068c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_PLUS,
22078c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_MINUS,
22088c2ecf20Sopenharmony_ci	WIIMOD_GUITAR_KEY_NUM,
22098c2ecf20Sopenharmony_ci};
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_cistatic const __u16 wiimod_guitar_map[] = {
22128c2ecf20Sopenharmony_ci	BTN_1,			/* WIIMOD_GUITAR_KEY_G */
22138c2ecf20Sopenharmony_ci	BTN_2,			/* WIIMOD_GUITAR_KEY_R */
22148c2ecf20Sopenharmony_ci	BTN_3,			/* WIIMOD_GUITAR_KEY_Y */
22158c2ecf20Sopenharmony_ci	BTN_4,			/* WIIMOD_GUITAR_KEY_B */
22168c2ecf20Sopenharmony_ci	BTN_5,			/* WIIMOD_GUITAR_KEY_O */
22178c2ecf20Sopenharmony_ci	BTN_DPAD_UP,		/* WIIMOD_GUITAR_KEY_UP */
22188c2ecf20Sopenharmony_ci	BTN_DPAD_DOWN,		/* WIIMOD_GUITAR_KEY_DOWN */
22198c2ecf20Sopenharmony_ci	BTN_START,		/* WIIMOD_GUITAR_KEY_PLUS */
22208c2ecf20Sopenharmony_ci	BTN_SELECT,		/* WIIMOD_GUITAR_KEY_MINUS */
22218c2ecf20Sopenharmony_ci};
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_cistatic void wiimod_guitar_in_ext(struct wiimote_data *wdata, const __u8 *ext)
22248c2ecf20Sopenharmony_ci{
22258c2ecf20Sopenharmony_ci	__u8 sx, sy, tb, wb, bd, bm, bp, bo, br, bb, bg, by, bu;
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
22288c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22298c2ecf20Sopenharmony_ci	 *    1   |  0  |  0  |              SX <5:0>             |
22308c2ecf20Sopenharmony_ci	 *    2   |  0  |  0  |              SY <5:0>             |
22318c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----------------------------+
22328c2ecf20Sopenharmony_ci	 *    3   |  0  |  0  |  0  |      TB <4:0>               |
22338c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----------------------------+
22348c2ecf20Sopenharmony_ci	 *    4   |  0  |  0  |  0  |      WB <4:0>               |
22358c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22368c2ecf20Sopenharmony_ci	 *    5   |  1  | BD  |  1  | B-  |  1  | B+  |  1  |  1  |
22378c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22388c2ecf20Sopenharmony_ci	 *    6   | BO  | BR  | BB  | BG  | BY  |  1  |  1  | BU  |
22398c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22408c2ecf20Sopenharmony_ci	 * All buttons are 0 if pressed
22418c2ecf20Sopenharmony_ci	 *
22428c2ecf20Sopenharmony_ci	 * With Motion+ enabled, it will look like this:
22438c2ecf20Sopenharmony_ci	 *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
22448c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22458c2ecf20Sopenharmony_ci	 *    1   |  0  |  0  |              SX <5:1>       | BU  |
22468c2ecf20Sopenharmony_ci	 *    2   |  0  |  0  |              SY <5:1>       |  1  |
22478c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----------------------+-----+
22488c2ecf20Sopenharmony_ci	 *    3   |  0  |  0  |  0  |      TB <4:0>               |
22498c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----------------------------+
22508c2ecf20Sopenharmony_ci	 *    4   |  0  |  0  |  0  |      WB <4:0>               |
22518c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22528c2ecf20Sopenharmony_ci	 *    5   |  1  | BD  |  1  | B-  |  1  | B+  |  1  |XXXXX|
22538c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22548c2ecf20Sopenharmony_ci	 *    6   | BO  | BR  | BB  | BG  | BY  |  1  |XXXXX|XXXXX|
22558c2ecf20Sopenharmony_ci	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
22568c2ecf20Sopenharmony_ci	 */
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	sx = ext[0] & 0x3f;
22598c2ecf20Sopenharmony_ci	sy = ext[1] & 0x3f;
22608c2ecf20Sopenharmony_ci	tb = ext[2] & 0x1f;
22618c2ecf20Sopenharmony_ci	wb = ext[3] & 0x1f;
22628c2ecf20Sopenharmony_ci	bd = !(ext[4] & 0x40);
22638c2ecf20Sopenharmony_ci	bm = !(ext[4] & 0x10);
22648c2ecf20Sopenharmony_ci	bp = !(ext[4] & 0x04);
22658c2ecf20Sopenharmony_ci	bo = !(ext[5] & 0x80);
22668c2ecf20Sopenharmony_ci	br = !(ext[5] & 0x40);
22678c2ecf20Sopenharmony_ci	bb = !(ext[5] & 0x20);
22688c2ecf20Sopenharmony_ci	bg = !(ext[5] & 0x10);
22698c2ecf20Sopenharmony_ci	by = !(ext[5] & 0x08);
22708c2ecf20Sopenharmony_ci	bu = !(ext[5] & 0x01);
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
22738c2ecf20Sopenharmony_ci		bu = !(ext[0] & 0x01);
22748c2ecf20Sopenharmony_ci		sx &= 0x3e;
22758c2ecf20Sopenharmony_ci		sy &= 0x3e;
22768c2ecf20Sopenharmony_ci	}
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_X, sx - 0x20);
22798c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20);
22808c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT0X, tb);
22818c2ecf20Sopenharmony_ci	input_report_abs(wdata->extension.input, ABS_HAT1X, wb - 0x10);
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
22848c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_G],
22858c2ecf20Sopenharmony_ci			 bg);
22868c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
22878c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_R],
22888c2ecf20Sopenharmony_ci			 br);
22898c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
22908c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_Y],
22918c2ecf20Sopenharmony_ci			 by);
22928c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
22938c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_B],
22948c2ecf20Sopenharmony_ci			 bb);
22958c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
22968c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_O],
22978c2ecf20Sopenharmony_ci			 bo);
22988c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
22998c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_UP],
23008c2ecf20Sopenharmony_ci			 bu);
23018c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
23028c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_DOWN],
23038c2ecf20Sopenharmony_ci			 bd);
23048c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
23058c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_PLUS],
23068c2ecf20Sopenharmony_ci			 bp);
23078c2ecf20Sopenharmony_ci	input_report_key(wdata->extension.input,
23088c2ecf20Sopenharmony_ci			 wiimod_guitar_map[WIIMOD_GUITAR_KEY_MINUS],
23098c2ecf20Sopenharmony_ci			 bm);
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	input_sync(wdata->extension.input);
23128c2ecf20Sopenharmony_ci}
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_cistatic int wiimod_guitar_open(struct input_dev *dev)
23158c2ecf20Sopenharmony_ci{
23168c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
23178c2ecf20Sopenharmony_ci	unsigned long flags;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
23208c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
23218c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
23228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	return 0;
23258c2ecf20Sopenharmony_ci}
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_cistatic void wiimod_guitar_close(struct input_dev *dev)
23288c2ecf20Sopenharmony_ci{
23298c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
23308c2ecf20Sopenharmony_ci	unsigned long flags;
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
23338c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
23348c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
23358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
23368c2ecf20Sopenharmony_ci}
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_cistatic int wiimod_guitar_probe(const struct wiimod_ops *ops,
23398c2ecf20Sopenharmony_ci			       struct wiimote_data *wdata)
23408c2ecf20Sopenharmony_ci{
23418c2ecf20Sopenharmony_ci	int ret, i;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	wdata->extension.input = input_allocate_device();
23448c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
23458c2ecf20Sopenharmony_ci		return -ENOMEM;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->extension.input, wdata);
23488c2ecf20Sopenharmony_ci	wdata->extension.input->open = wiimod_guitar_open;
23498c2ecf20Sopenharmony_ci	wdata->extension.input->close = wiimod_guitar_close;
23508c2ecf20Sopenharmony_ci	wdata->extension.input->dev.parent = &wdata->hdev->dev;
23518c2ecf20Sopenharmony_ci	wdata->extension.input->id.bustype = wdata->hdev->bus;
23528c2ecf20Sopenharmony_ci	wdata->extension.input->id.vendor = wdata->hdev->vendor;
23538c2ecf20Sopenharmony_ci	wdata->extension.input->id.product = wdata->hdev->product;
23548c2ecf20Sopenharmony_ci	wdata->extension.input->id.version = wdata->hdev->version;
23558c2ecf20Sopenharmony_ci	wdata->extension.input->name = WIIMOTE_NAME " Guitar";
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	set_bit(EV_KEY, wdata->extension.input->evbit);
23588c2ecf20Sopenharmony_ci	for (i = 0; i < WIIMOD_GUITAR_KEY_NUM; ++i)
23598c2ecf20Sopenharmony_ci		set_bit(wiimod_guitar_map[i],
23608c2ecf20Sopenharmony_ci			wdata->extension.input->keybit);
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->extension.input->evbit);
23638c2ecf20Sopenharmony_ci	set_bit(ABS_X, wdata->extension.input->absbit);
23648c2ecf20Sopenharmony_ci	set_bit(ABS_Y, wdata->extension.input->absbit);
23658c2ecf20Sopenharmony_ci	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
23668c2ecf20Sopenharmony_ci	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
23678c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
23688c2ecf20Sopenharmony_ci			     ABS_X, -32, 31, 1, 1);
23698c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
23708c2ecf20Sopenharmony_ci			     ABS_Y, -32, 31, 1, 1);
23718c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
23728c2ecf20Sopenharmony_ci			     ABS_HAT0X, 0, 0x1f, 1, 1);
23738c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->extension.input,
23748c2ecf20Sopenharmony_ci			     ABS_HAT1X, 0, 0x0f, 1, 1);
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->extension.input);
23778c2ecf20Sopenharmony_ci	if (ret)
23788c2ecf20Sopenharmony_ci		goto err_free;
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci	return 0;
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_cierr_free:
23838c2ecf20Sopenharmony_ci	input_free_device(wdata->extension.input);
23848c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
23858c2ecf20Sopenharmony_ci	return ret;
23868c2ecf20Sopenharmony_ci}
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_cistatic void wiimod_guitar_remove(const struct wiimod_ops *ops,
23898c2ecf20Sopenharmony_ci				 struct wiimote_data *wdata)
23908c2ecf20Sopenharmony_ci{
23918c2ecf20Sopenharmony_ci	if (!wdata->extension.input)
23928c2ecf20Sopenharmony_ci		return;
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_ci	input_unregister_device(wdata->extension.input);
23958c2ecf20Sopenharmony_ci	wdata->extension.input = NULL;
23968c2ecf20Sopenharmony_ci}
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_guitar = {
23998c2ecf20Sopenharmony_ci	.flags = 0,
24008c2ecf20Sopenharmony_ci	.arg = 0,
24018c2ecf20Sopenharmony_ci	.probe = wiimod_guitar_probe,
24028c2ecf20Sopenharmony_ci	.remove = wiimod_guitar_remove,
24038c2ecf20Sopenharmony_ci	.in_ext = wiimod_guitar_in_ext,
24048c2ecf20Sopenharmony_ci};
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_ci/*
24078c2ecf20Sopenharmony_ci * Builtin Motion Plus
24088c2ecf20Sopenharmony_ci * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
24098c2ecf20Sopenharmony_ci * disables polling for Motion-Plus. This should be set only for devices which
24108c2ecf20Sopenharmony_ci * don't allow MP hotplugging.
24118c2ecf20Sopenharmony_ci */
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_cistatic int wiimod_builtin_mp_probe(const struct wiimod_ops *ops,
24148c2ecf20Sopenharmony_ci				   struct wiimote_data *wdata)
24158c2ecf20Sopenharmony_ci{
24168c2ecf20Sopenharmony_ci	unsigned long flags;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
24198c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_BUILTIN_MP;
24208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	return 0;
24238c2ecf20Sopenharmony_ci}
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_cistatic void wiimod_builtin_mp_remove(const struct wiimod_ops *ops,
24268c2ecf20Sopenharmony_ci				     struct wiimote_data *wdata)
24278c2ecf20Sopenharmony_ci{
24288c2ecf20Sopenharmony_ci	unsigned long flags;
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
24318c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_BUILTIN_MP;
24328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
24338c2ecf20Sopenharmony_ci}
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_builtin_mp = {
24368c2ecf20Sopenharmony_ci	.flags = 0,
24378c2ecf20Sopenharmony_ci	.arg = 0,
24388c2ecf20Sopenharmony_ci	.probe = wiimod_builtin_mp_probe,
24398c2ecf20Sopenharmony_ci	.remove = wiimod_builtin_mp_remove,
24408c2ecf20Sopenharmony_ci};
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci/*
24438c2ecf20Sopenharmony_ci * No Motion Plus
24448c2ecf20Sopenharmony_ci * This module simply sets the WIIPROTO_FLAG_NO_MP protocol flag which
24458c2ecf20Sopenharmony_ci * disables motion-plus. This is needed for devices that advertise this but we
24468c2ecf20Sopenharmony_ci * don't know how to use it (or whether it is actually present).
24478c2ecf20Sopenharmony_ci */
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_cistatic int wiimod_no_mp_probe(const struct wiimod_ops *ops,
24508c2ecf20Sopenharmony_ci			      struct wiimote_data *wdata)
24518c2ecf20Sopenharmony_ci{
24528c2ecf20Sopenharmony_ci	unsigned long flags;
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
24558c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_NO_MP;
24568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	return 0;
24598c2ecf20Sopenharmony_ci}
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_cistatic void wiimod_no_mp_remove(const struct wiimod_ops *ops,
24628c2ecf20Sopenharmony_ci				struct wiimote_data *wdata)
24638c2ecf20Sopenharmony_ci{
24648c2ecf20Sopenharmony_ci	unsigned long flags;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
24678c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_NO_MP;
24688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
24698c2ecf20Sopenharmony_ci}
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_no_mp = {
24728c2ecf20Sopenharmony_ci	.flags = 0,
24738c2ecf20Sopenharmony_ci	.arg = 0,
24748c2ecf20Sopenharmony_ci	.probe = wiimod_no_mp_probe,
24758c2ecf20Sopenharmony_ci	.remove = wiimod_no_mp_remove,
24768c2ecf20Sopenharmony_ci};
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci/*
24798c2ecf20Sopenharmony_ci * Motion Plus
24808c2ecf20Sopenharmony_ci * The Motion Plus extension provides rotation sensors (gyro) as a small
24818c2ecf20Sopenharmony_ci * extension device for Wii Remotes. Many devices have them built-in so
24828c2ecf20Sopenharmony_ci * you cannot see them from the outside.
24838c2ecf20Sopenharmony_ci * Motion Plus extensions are special because they are on a separate extension
24848c2ecf20Sopenharmony_ci * port and allow other extensions to be used simultaneously. This is all
24858c2ecf20Sopenharmony_ci * handled by the Wiimote Core so we don't have to deal with it.
24868c2ecf20Sopenharmony_ci */
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_cistatic void wiimod_mp_in_mp(struct wiimote_data *wdata, const __u8 *ext)
24898c2ecf20Sopenharmony_ci{
24908c2ecf20Sopenharmony_ci	__s32 x, y, z;
24918c2ecf20Sopenharmony_ci
24928c2ecf20Sopenharmony_ci	/*        |   8    7    6    5    4    3 |  2  |  1  |
24938c2ecf20Sopenharmony_ci	 *   -----+------------------------------+-----+-----+
24948c2ecf20Sopenharmony_ci	 *    1   |               Yaw Speed <7:0>            |
24958c2ecf20Sopenharmony_ci	 *    2   |              Roll Speed <7:0>            |
24968c2ecf20Sopenharmony_ci	 *    3   |             Pitch Speed <7:0>            |
24978c2ecf20Sopenharmony_ci	 *   -----+------------------------------+-----+-----+
24988c2ecf20Sopenharmony_ci	 *    4   |       Yaw Speed <13:8>       | Yaw |Pitch|
24998c2ecf20Sopenharmony_ci	 *   -----+------------------------------+-----+-----+
25008c2ecf20Sopenharmony_ci	 *    5   |      Roll Speed <13:8>       |Roll | Ext |
25018c2ecf20Sopenharmony_ci	 *   -----+------------------------------+-----+-----+
25028c2ecf20Sopenharmony_ci	 *    6   |     Pitch Speed <13:8>       |  1  |  0  |
25038c2ecf20Sopenharmony_ci	 *   -----+------------------------------+-----+-----+
25048c2ecf20Sopenharmony_ci	 * The single bits Yaw, Roll, Pitch in the lower right corner specify
25058c2ecf20Sopenharmony_ci	 * whether the wiimote is rotating fast (0) or slow (1). Speed for slow
25068c2ecf20Sopenharmony_ci	 * roation is 8192/440 units / deg/s and for fast rotation 8192/2000
25078c2ecf20Sopenharmony_ci	 * units / deg/s. To get a linear scale for fast rotation we multiply
25088c2ecf20Sopenharmony_ci	 * by 2000/440 = ~4.5454 and scale both fast and slow by 9 to match the
25098c2ecf20Sopenharmony_ci	 * previous scale reported by this driver.
25108c2ecf20Sopenharmony_ci	 * This leaves a linear scale with 8192*9/440 (~167.564) units / deg/s.
25118c2ecf20Sopenharmony_ci	 * If the wiimote is not rotating the sensor reports 2^13 = 8192.
25128c2ecf20Sopenharmony_ci	 * Ext specifies whether an extension is connected to the motionp.
25138c2ecf20Sopenharmony_ci	 * which is parsed by wiimote-core.
25148c2ecf20Sopenharmony_ci	 */
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_ci	x = ext[0];
25178c2ecf20Sopenharmony_ci	y = ext[1];
25188c2ecf20Sopenharmony_ci	z = ext[2];
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	x |= (((__u16)ext[3]) << 6) & 0xff00;
25218c2ecf20Sopenharmony_ci	y |= (((__u16)ext[4]) << 6) & 0xff00;
25228c2ecf20Sopenharmony_ci	z |= (((__u16)ext[5]) << 6) & 0xff00;
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_ci	x -= 8192;
25258c2ecf20Sopenharmony_ci	y -= 8192;
25268c2ecf20Sopenharmony_ci	z -= 8192;
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci	if (!(ext[3] & 0x02))
25298c2ecf20Sopenharmony_ci		x = (x * 2000 * 9) / 440;
25308c2ecf20Sopenharmony_ci	else
25318c2ecf20Sopenharmony_ci		x *= 9;
25328c2ecf20Sopenharmony_ci	if (!(ext[4] & 0x02))
25338c2ecf20Sopenharmony_ci		y = (y * 2000 * 9) / 440;
25348c2ecf20Sopenharmony_ci	else
25358c2ecf20Sopenharmony_ci		y *= 9;
25368c2ecf20Sopenharmony_ci	if (!(ext[3] & 0x01))
25378c2ecf20Sopenharmony_ci		z = (z * 2000 * 9) / 440;
25388c2ecf20Sopenharmony_ci	else
25398c2ecf20Sopenharmony_ci		z *= 9;
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	input_report_abs(wdata->mp, ABS_RX, x);
25428c2ecf20Sopenharmony_ci	input_report_abs(wdata->mp, ABS_RY, y);
25438c2ecf20Sopenharmony_ci	input_report_abs(wdata->mp, ABS_RZ, z);
25448c2ecf20Sopenharmony_ci	input_sync(wdata->mp);
25458c2ecf20Sopenharmony_ci}
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_cistatic int wiimod_mp_open(struct input_dev *dev)
25488c2ecf20Sopenharmony_ci{
25498c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
25508c2ecf20Sopenharmony_ci	unsigned long flags;
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
25538c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_MP_USED;
25548c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
25558c2ecf20Sopenharmony_ci	__wiimote_schedule(wdata);
25568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	return 0;
25598c2ecf20Sopenharmony_ci}
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_cistatic void wiimod_mp_close(struct input_dev *dev)
25628c2ecf20Sopenharmony_ci{
25638c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = input_get_drvdata(dev);
25648c2ecf20Sopenharmony_ci	unsigned long flags;
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
25678c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
25688c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
25698c2ecf20Sopenharmony_ci	__wiimote_schedule(wdata);
25708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
25718c2ecf20Sopenharmony_ci}
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_cistatic int wiimod_mp_probe(const struct wiimod_ops *ops,
25748c2ecf20Sopenharmony_ci			   struct wiimote_data *wdata)
25758c2ecf20Sopenharmony_ci{
25768c2ecf20Sopenharmony_ci	int ret;
25778c2ecf20Sopenharmony_ci
25788c2ecf20Sopenharmony_ci	wdata->mp = input_allocate_device();
25798c2ecf20Sopenharmony_ci	if (!wdata->mp)
25808c2ecf20Sopenharmony_ci		return -ENOMEM;
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci	input_set_drvdata(wdata->mp, wdata);
25838c2ecf20Sopenharmony_ci	wdata->mp->open = wiimod_mp_open;
25848c2ecf20Sopenharmony_ci	wdata->mp->close = wiimod_mp_close;
25858c2ecf20Sopenharmony_ci	wdata->mp->dev.parent = &wdata->hdev->dev;
25868c2ecf20Sopenharmony_ci	wdata->mp->id.bustype = wdata->hdev->bus;
25878c2ecf20Sopenharmony_ci	wdata->mp->id.vendor = wdata->hdev->vendor;
25888c2ecf20Sopenharmony_ci	wdata->mp->id.product = wdata->hdev->product;
25898c2ecf20Sopenharmony_ci	wdata->mp->id.version = wdata->hdev->version;
25908c2ecf20Sopenharmony_ci	wdata->mp->name = WIIMOTE_NAME " Motion Plus";
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	set_bit(EV_ABS, wdata->mp->evbit);
25938c2ecf20Sopenharmony_ci	set_bit(ABS_RX, wdata->mp->absbit);
25948c2ecf20Sopenharmony_ci	set_bit(ABS_RY, wdata->mp->absbit);
25958c2ecf20Sopenharmony_ci	set_bit(ABS_RZ, wdata->mp->absbit);
25968c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->mp,
25978c2ecf20Sopenharmony_ci			     ABS_RX, -16000, 16000, 4, 8);
25988c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->mp,
25998c2ecf20Sopenharmony_ci			     ABS_RY, -16000, 16000, 4, 8);
26008c2ecf20Sopenharmony_ci	input_set_abs_params(wdata->mp,
26018c2ecf20Sopenharmony_ci			     ABS_RZ, -16000, 16000, 4, 8);
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_ci	ret = input_register_device(wdata->mp);
26048c2ecf20Sopenharmony_ci	if (ret)
26058c2ecf20Sopenharmony_ci		goto err_free;
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci	return 0;
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_cierr_free:
26108c2ecf20Sopenharmony_ci	input_free_device(wdata->mp);
26118c2ecf20Sopenharmony_ci	wdata->mp = NULL;
26128c2ecf20Sopenharmony_ci	return ret;
26138c2ecf20Sopenharmony_ci}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_cistatic void wiimod_mp_remove(const struct wiimod_ops *ops,
26168c2ecf20Sopenharmony_ci			     struct wiimote_data *wdata)
26178c2ecf20Sopenharmony_ci{
26188c2ecf20Sopenharmony_ci	if (!wdata->mp)
26198c2ecf20Sopenharmony_ci		return;
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	input_unregister_device(wdata->mp);
26228c2ecf20Sopenharmony_ci	wdata->mp = NULL;
26238c2ecf20Sopenharmony_ci}
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ciconst struct wiimod_ops wiimod_mp = {
26268c2ecf20Sopenharmony_ci	.flags = 0,
26278c2ecf20Sopenharmony_ci	.arg = 0,
26288c2ecf20Sopenharmony_ci	.probe = wiimod_mp_probe,
26298c2ecf20Sopenharmony_ci	.remove = wiimod_mp_remove,
26308c2ecf20Sopenharmony_ci	.in_mp = wiimod_mp_in_mp,
26318c2ecf20Sopenharmony_ci};
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci/* module table */
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_cistatic const struct wiimod_ops wiimod_dummy;
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ciconst struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
26388c2ecf20Sopenharmony_ci	[WIIMOD_KEYS] = &wiimod_keys,
26398c2ecf20Sopenharmony_ci	[WIIMOD_RUMBLE] = &wiimod_rumble,
26408c2ecf20Sopenharmony_ci	[WIIMOD_BATTERY] = &wiimod_battery,
26418c2ecf20Sopenharmony_ci	[WIIMOD_LED1] = &wiimod_leds[0],
26428c2ecf20Sopenharmony_ci	[WIIMOD_LED2] = &wiimod_leds[1],
26438c2ecf20Sopenharmony_ci	[WIIMOD_LED3] = &wiimod_leds[2],
26448c2ecf20Sopenharmony_ci	[WIIMOD_LED4] = &wiimod_leds[3],
26458c2ecf20Sopenharmony_ci	[WIIMOD_ACCEL] = &wiimod_accel,
26468c2ecf20Sopenharmony_ci	[WIIMOD_IR] = &wiimod_ir,
26478c2ecf20Sopenharmony_ci	[WIIMOD_BUILTIN_MP] = &wiimod_builtin_mp,
26488c2ecf20Sopenharmony_ci	[WIIMOD_NO_MP] = &wiimod_no_mp,
26498c2ecf20Sopenharmony_ci};
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ciconst struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
26528c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_NONE] = &wiimod_dummy,
26538c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy,
26548c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_NUNCHUK] = &wiimod_nunchuk,
26558c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic,
26568c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
26578c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro,
26588c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_DRUMS] = &wiimod_drums,
26598c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_GUITAR] = &wiimod_guitar,
26608c2ecf20Sopenharmony_ci};
2661