18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * HID driver for Nintendo Wii / Wii U peripherals
48c2ecf20Sopenharmony_ci * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/completion.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/hid.h>
138c2ecf20Sopenharmony_ci#include <linux/input.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include "hid-ids.h"
188c2ecf20Sopenharmony_ci#include "hid-wiimote.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* output queue handling */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
238c2ecf20Sopenharmony_ci			    size_t count)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	__u8 *buf;
268c2ecf20Sopenharmony_ci	int ret;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (!hdev->ll_driver->output_report)
298c2ecf20Sopenharmony_ci		return -ENODEV;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	buf = kmemdup(buffer, count, GFP_KERNEL);
328c2ecf20Sopenharmony_ci	if (!buf)
338c2ecf20Sopenharmony_ci		return -ENOMEM;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ret = hid_hw_output_report(hdev, buf, count);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	kfree(buf);
388c2ecf20Sopenharmony_ci	return ret;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void wiimote_queue_worker(struct work_struct *work)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct wiimote_queue *queue = container_of(work, struct wiimote_queue,
448c2ecf20Sopenharmony_ci						   worker);
458c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = container_of(queue, struct wiimote_data,
468c2ecf20Sopenharmony_ci						  queue);
478c2ecf20Sopenharmony_ci	unsigned long flags;
488c2ecf20Sopenharmony_ci	int ret;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->queue.lock, flags);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	while (wdata->queue.head != wdata->queue.tail) {
538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&wdata->queue.lock, flags);
548c2ecf20Sopenharmony_ci		ret = wiimote_hid_send(wdata->hdev,
558c2ecf20Sopenharmony_ci				 wdata->queue.outq[wdata->queue.tail].data,
568c2ecf20Sopenharmony_ci				 wdata->queue.outq[wdata->queue.tail].size);
578c2ecf20Sopenharmony_ci		if (ret < 0) {
588c2ecf20Sopenharmony_ci			spin_lock_irqsave(&wdata->state.lock, flags);
598c2ecf20Sopenharmony_ci			wiimote_cmd_abort(wdata);
608c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&wdata->state.lock, flags);
618c2ecf20Sopenharmony_ci		}
628c2ecf20Sopenharmony_ci		spin_lock_irqsave(&wdata->queue.lock, flags);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->queue.lock, flags);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
718c2ecf20Sopenharmony_ci								size_t count)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	unsigned long flags;
748c2ecf20Sopenharmony_ci	__u8 newhead;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (count > HID_MAX_BUFFER_SIZE) {
778c2ecf20Sopenharmony_ci		hid_warn(wdata->hdev, "Sending too large output report\n");
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		spin_lock_irqsave(&wdata->queue.lock, flags);
808c2ecf20Sopenharmony_ci		goto out_error;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/*
848c2ecf20Sopenharmony_ci	 * Copy new request into our output queue and check whether the
858c2ecf20Sopenharmony_ci	 * queue is full. If it is full, discard this request.
868c2ecf20Sopenharmony_ci	 * If it is empty we need to start a new worker that will
878c2ecf20Sopenharmony_ci	 * send out the buffer to the hid device.
888c2ecf20Sopenharmony_ci	 * If the queue is not empty, then there must be a worker
898c2ecf20Sopenharmony_ci	 * that is currently sending out our buffer and this worker
908c2ecf20Sopenharmony_ci	 * will reschedule itself until the queue is empty.
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->queue.lock, flags);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	memcpy(wdata->queue.outq[wdata->queue.head].data, buffer, count);
968c2ecf20Sopenharmony_ci	wdata->queue.outq[wdata->queue.head].size = count;
978c2ecf20Sopenharmony_ci	newhead = (wdata->queue.head + 1) % WIIMOTE_BUFSIZE;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (wdata->queue.head == wdata->queue.tail) {
1008c2ecf20Sopenharmony_ci		wdata->queue.head = newhead;
1018c2ecf20Sopenharmony_ci		schedule_work(&wdata->queue.worker);
1028c2ecf20Sopenharmony_ci	} else if (newhead != wdata->queue.tail) {
1038c2ecf20Sopenharmony_ci		wdata->queue.head = newhead;
1048c2ecf20Sopenharmony_ci	} else {
1058c2ecf20Sopenharmony_ci		hid_warn(wdata->hdev, "Output queue is full");
1068c2ecf20Sopenharmony_ci		goto out_error;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	goto out_unlock;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciout_error:
1128c2ecf20Sopenharmony_ci	wiimote_cmd_abort(wdata);
1138c2ecf20Sopenharmony_ciout_unlock:
1148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->queue.lock, flags);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * This sets the rumble bit on the given output report if rumble is
1198c2ecf20Sopenharmony_ci * currently enabled.
1208c2ecf20Sopenharmony_ci * \cmd1 must point to the second byte in the output report => &cmd[1]
1218c2ecf20Sopenharmony_ci * This must be called on nearly every output report before passing it
1228c2ecf20Sopenharmony_ci * into the output queue!
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE)
1278c2ecf20Sopenharmony_ci		*cmd1 |= 0x01;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	__u8 cmd[2];
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	rumble = !!rumble;
1358c2ecf20Sopenharmony_ci	if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE))
1368c2ecf20Sopenharmony_ci		return;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (rumble)
1398c2ecf20Sopenharmony_ci		wdata->state.flags |= WIIPROTO_FLAG_RUMBLE;
1408c2ecf20Sopenharmony_ci	else
1418c2ecf20Sopenharmony_ci		wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_RUMBLE;
1448c2ecf20Sopenharmony_ci	cmd[1] = 0;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
1478c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_civoid wiiproto_req_leds(struct wiimote_data *wdata, int leds)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	__u8 cmd[2];
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	leds &= WIIPROTO_FLAGS_LEDS;
1558c2ecf20Sopenharmony_ci	if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds)
1568c2ecf20Sopenharmony_ci		return;
1578c2ecf20Sopenharmony_ci	wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_LED;
1608c2ecf20Sopenharmony_ci	cmd[1] = 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (leds & WIIPROTO_FLAG_LED1)
1638c2ecf20Sopenharmony_ci		cmd[1] |= 0x10;
1648c2ecf20Sopenharmony_ci	if (leds & WIIPROTO_FLAG_LED2)
1658c2ecf20Sopenharmony_ci		cmd[1] |= 0x20;
1668c2ecf20Sopenharmony_ci	if (leds & WIIPROTO_FLAG_LED3)
1678c2ecf20Sopenharmony_ci		cmd[1] |= 0x40;
1688c2ecf20Sopenharmony_ci	if (leds & WIIPROTO_FLAG_LED4)
1698c2ecf20Sopenharmony_ci		cmd[1] |= 0x80;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
1728c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * Check what peripherals of the wiimote are currently
1778c2ecf20Sopenharmony_ci * active and select a proper DRM that supports all of
1788c2ecf20Sopenharmony_ci * the requested data inputs.
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * Not all combinations are actually supported. The following
1818c2ecf20Sopenharmony_ci * combinations work only with limitations:
1828c2ecf20Sopenharmony_ci *  - IR cam in extended or full mode disables any data transmission
1838c2ecf20Sopenharmony_ci *    of extension controllers. There is no DRM mode that supports
1848c2ecf20Sopenharmony_ci *    extension bytes plus extended/full IR.
1858c2ecf20Sopenharmony_ci *  - IR cam with accelerometer and extension *_EXT8 is not supported.
1868c2ecf20Sopenharmony_ci *    However, all extensions that need *_EXT8 are devices that don't
1878c2ecf20Sopenharmony_ci *    support IR cameras. Hence, this shouldn't happen under normal
1888c2ecf20Sopenharmony_ci *    operation.
1898c2ecf20Sopenharmony_ci *  - *_EXT16 is only supported in combination with buttons and
1908c2ecf20Sopenharmony_ci *    accelerometer. No IR or similar can be active simultaneously. As
1918c2ecf20Sopenharmony_ci *    above, all modules that require it are mutually exclusive with
1928c2ecf20Sopenharmony_ci *    IR/etc. so this doesn't matter.
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic __u8 select_drm(struct wiimote_data *wdata)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	__u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
1978c2ecf20Sopenharmony_ci	bool ext;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) ||
2008c2ecf20Sopenharmony_ci	      (wdata->state.flags & WIIPROTO_FLAG_MP_USED);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* some 3rd-party balance-boards are hard-coded to KEE, *sigh* */
2038c2ecf20Sopenharmony_ci	if (wdata->state.devtype == WIIMOTE_DEV_BALANCE_BOARD) {
2048c2ecf20Sopenharmony_ci		if (ext)
2058c2ecf20Sopenharmony_ci			return WIIPROTO_REQ_DRM_KEE;
2068c2ecf20Sopenharmony_ci		else
2078c2ecf20Sopenharmony_ci			return WIIPROTO_REQ_DRM_K;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (ir == WIIPROTO_FLAG_IR_BASIC) {
2118c2ecf20Sopenharmony_ci		if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
2128c2ecf20Sopenharmony_ci			/* GEN10 and ealier devices bind IR formats to DRMs.
2138c2ecf20Sopenharmony_ci			 * Hence, we cannot use DRM_KAI here as it might be
2148c2ecf20Sopenharmony_ci			 * bound to IR_EXT. Use DRM_KAIE unconditionally so we
2158c2ecf20Sopenharmony_ci			 * work with all devices and our parsers can use the
2168c2ecf20Sopenharmony_ci			 * fixed formats, too. */
2178c2ecf20Sopenharmony_ci			return WIIPROTO_REQ_DRM_KAIE;
2188c2ecf20Sopenharmony_ci		} else {
2198c2ecf20Sopenharmony_ci			return WIIPROTO_REQ_DRM_KIE;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	} else if (ir == WIIPROTO_FLAG_IR_EXT) {
2228c2ecf20Sopenharmony_ci		return WIIPROTO_REQ_DRM_KAI;
2238c2ecf20Sopenharmony_ci	} else if (ir == WIIPROTO_FLAG_IR_FULL) {
2248c2ecf20Sopenharmony_ci		return WIIPROTO_REQ_DRM_SKAI1;
2258c2ecf20Sopenharmony_ci	} else {
2268c2ecf20Sopenharmony_ci		if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
2278c2ecf20Sopenharmony_ci			if (ext)
2288c2ecf20Sopenharmony_ci				return WIIPROTO_REQ_DRM_KAE;
2298c2ecf20Sopenharmony_ci			else
2308c2ecf20Sopenharmony_ci				return WIIPROTO_REQ_DRM_KA;
2318c2ecf20Sopenharmony_ci		} else {
2328c2ecf20Sopenharmony_ci			if (ext)
2338c2ecf20Sopenharmony_ci				return WIIPROTO_REQ_DRM_KEE;
2348c2ecf20Sopenharmony_ci			else
2358c2ecf20Sopenharmony_ci				return WIIPROTO_REQ_DRM_K;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	__u8 cmd[3];
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_DRM_LOCKED)
2458c2ecf20Sopenharmony_ci		drm = wdata->state.drm;
2468c2ecf20Sopenharmony_ci	else if (drm == WIIPROTO_REQ_NULL)
2478c2ecf20Sopenharmony_ci		drm = select_drm(wdata);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_DRM;
2508c2ecf20Sopenharmony_ci	cmd[1] = 0;
2518c2ecf20Sopenharmony_ci	cmd[2] = drm;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	wdata->state.drm = drm;
2548c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
2558c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_civoid wiiproto_req_status(struct wiimote_data *wdata)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	__u8 cmd[2];
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_SREQ;
2638c2ecf20Sopenharmony_ci	cmd[1] = 0;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
2668c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_civoid wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	accel = !!accel;
2728c2ecf20Sopenharmony_ci	if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
2738c2ecf20Sopenharmony_ci		return;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (accel)
2768c2ecf20Sopenharmony_ci		wdata->state.flags |= WIIPROTO_FLAG_ACCEL;
2778c2ecf20Sopenharmony_ci	else
2788c2ecf20Sopenharmony_ci		wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_civoid wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	__u8 cmd[2];
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_IR1;
2888c2ecf20Sopenharmony_ci	cmd[1] = flags;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
2918c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_civoid wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	__u8 cmd[2];
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_IR2;
2998c2ecf20Sopenharmony_ci	cmd[1] = flags;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
3028c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci#define wiiproto_req_wreg(wdata, os, buf, sz) \
3068c2ecf20Sopenharmony_ci			wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci#define wiiproto_req_weeprom(wdata, os, buf, sz) \
3098c2ecf20Sopenharmony_ci			wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
3128c2ecf20Sopenharmony_ci				__u32 offset, const __u8 *buf, __u8 size)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	__u8 cmd[22];
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (size > 16 || size == 0) {
3178c2ecf20Sopenharmony_ci		hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size);
3188c2ecf20Sopenharmony_ci		return;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(cmd));
3228c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_WMEM;
3238c2ecf20Sopenharmony_ci	cmd[2] = (offset >> 16) & 0xff;
3248c2ecf20Sopenharmony_ci	cmd[3] = (offset >> 8) & 0xff;
3258c2ecf20Sopenharmony_ci	cmd[4] = offset & 0xff;
3268c2ecf20Sopenharmony_ci	cmd[5] = size;
3278c2ecf20Sopenharmony_ci	memcpy(&cmd[6], buf, size);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!eeprom)
3308c2ecf20Sopenharmony_ci		cmd[1] |= 0x04;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
3338c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_civoid wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset,
3378c2ecf20Sopenharmony_ci								__u16 size)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	__u8 cmd[7];
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (size == 0) {
3428c2ecf20Sopenharmony_ci		hid_warn(wdata->hdev, "Invalid length %d rmem request\n", size);
3438c2ecf20Sopenharmony_ci		return;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	cmd[0] = WIIPROTO_REQ_RMEM;
3478c2ecf20Sopenharmony_ci	cmd[1] = 0;
3488c2ecf20Sopenharmony_ci	cmd[2] = (offset >> 16) & 0xff;
3498c2ecf20Sopenharmony_ci	cmd[3] = (offset >> 8) & 0xff;
3508c2ecf20Sopenharmony_ci	cmd[4] = offset & 0xff;
3518c2ecf20Sopenharmony_ci	cmd[5] = (size >> 8) & 0xff;
3528c2ecf20Sopenharmony_ci	cmd[6] = size & 0xff;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (!eeprom)
3558c2ecf20Sopenharmony_ci		cmd[1] |= 0x04;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	wiiproto_keep_rumble(wdata, &cmd[1]);
3588c2ecf20Sopenharmony_ci	wiimote_queue(wdata, cmd, sizeof(cmd));
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/* requries the cmd-mutex to be held */
3628c2ecf20Sopenharmony_ciint wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
3638c2ecf20Sopenharmony_ci						const __u8 *wmem, __u8 size)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	unsigned long flags;
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
3698c2ecf20Sopenharmony_ci	wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0);
3708c2ecf20Sopenharmony_ci	wiiproto_req_wreg(wdata, offset, wmem, size);
3718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ret = wiimote_cmd_wait(wdata);
3748c2ecf20Sopenharmony_ci	if (!ret && wdata->state.cmd_err)
3758c2ecf20Sopenharmony_ci		ret = -EIO;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return ret;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci/* requries the cmd-mutex to be held */
3818c2ecf20Sopenharmony_cissize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem,
3828c2ecf20Sopenharmony_ci								__u8 size)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	unsigned long flags;
3858c2ecf20Sopenharmony_ci	ssize_t ret;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
3888c2ecf20Sopenharmony_ci	wdata->state.cmd_read_size = size;
3898c2ecf20Sopenharmony_ci	wdata->state.cmd_read_buf = rmem;
3908c2ecf20Sopenharmony_ci	wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, offset & 0xffff);
3918c2ecf20Sopenharmony_ci	wiiproto_req_rreg(wdata, offset, size);
3928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	ret = wiimote_cmd_wait(wdata);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
3978c2ecf20Sopenharmony_ci	wdata->state.cmd_read_buf = NULL;
3988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (!ret) {
4018c2ecf20Sopenharmony_ci		if (wdata->state.cmd_read_size == 0)
4028c2ecf20Sopenharmony_ci			ret = -EIO;
4038c2ecf20Sopenharmony_ci		else
4048c2ecf20Sopenharmony_ci			ret = wdata->state.cmd_read_size;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return ret;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci/* requires the cmd-mutex to be held */
4118c2ecf20Sopenharmony_cistatic int wiimote_cmd_init_ext(struct wiimote_data *wdata)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	__u8 wmem;
4148c2ecf20Sopenharmony_ci	int ret;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* initialize extension */
4178c2ecf20Sopenharmony_ci	wmem = 0x55;
4188c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xa400f0, &wmem, sizeof(wmem));
4198c2ecf20Sopenharmony_ci	if (ret)
4208c2ecf20Sopenharmony_ci		return ret;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* disable default encryption */
4238c2ecf20Sopenharmony_ci	wmem = 0x0;
4248c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xa400fb, &wmem, sizeof(wmem));
4258c2ecf20Sopenharmony_ci	if (ret)
4268c2ecf20Sopenharmony_ci		return ret;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/* requires the cmd-mutex to be held */
4328c2ecf20Sopenharmony_cistatic __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	int ret;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/* read extension ID */
4378c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
4388c2ecf20Sopenharmony_ci	if (ret != 6)
4398c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_NONE;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	hid_dbg(wdata->hdev, "extension ID: %6phC\n", rmem);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
4448c2ecf20Sopenharmony_ci	    rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
4458c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_NONE;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (rmem[4] == 0x00 && rmem[5] == 0x00)
4488c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_NUNCHUK;
4498c2ecf20Sopenharmony_ci	if (rmem[4] == 0x01 && rmem[5] == 0x01)
4508c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_CLASSIC_CONTROLLER;
4518c2ecf20Sopenharmony_ci	if (rmem[4] == 0x04 && rmem[5] == 0x02)
4528c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_BALANCE_BOARD;
4538c2ecf20Sopenharmony_ci	if (rmem[4] == 0x01 && rmem[5] == 0x20)
4548c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_PRO_CONTROLLER;
4558c2ecf20Sopenharmony_ci	if (rmem[0] == 0x01 && rmem[1] == 0x00 &&
4568c2ecf20Sopenharmony_ci	    rmem[4] == 0x01 && rmem[5] == 0x03)
4578c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_DRUMS;
4588c2ecf20Sopenharmony_ci	if (rmem[0] == 0x00 && rmem[1] == 0x00 &&
4598c2ecf20Sopenharmony_ci	    rmem[4] == 0x01 && rmem[5] == 0x03)
4608c2ecf20Sopenharmony_ci		return WIIMOTE_EXT_GUITAR;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return WIIMOTE_EXT_UNKNOWN;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/* requires the cmd-mutex to be held */
4668c2ecf20Sopenharmony_cistatic int wiimote_cmd_init_mp(struct wiimote_data *wdata)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	__u8 wmem;
4698c2ecf20Sopenharmony_ci	int ret;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* initialize MP */
4728c2ecf20Sopenharmony_ci	wmem = 0x55;
4738c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xa600f0, &wmem, sizeof(wmem));
4748c2ecf20Sopenharmony_ci	if (ret)
4758c2ecf20Sopenharmony_ci		return ret;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* disable default encryption */
4788c2ecf20Sopenharmony_ci	wmem = 0x0;
4798c2ecf20Sopenharmony_ci	ret = wiimote_cmd_write(wdata, 0xa600fb, &wmem, sizeof(wmem));
4808c2ecf20Sopenharmony_ci	if (ret)
4818c2ecf20Sopenharmony_ci		return ret;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	return 0;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/* requires the cmd-mutex to be held */
4878c2ecf20Sopenharmony_cistatic bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	__u8 wmem;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* map MP with correct pass-through mode */
4928c2ecf20Sopenharmony_ci	switch (exttype) {
4938c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_CLASSIC_CONTROLLER:
4948c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_DRUMS:
4958c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_GUITAR:
4968c2ecf20Sopenharmony_ci		wmem = 0x07;
4978c2ecf20Sopenharmony_ci		break;
4988c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_NUNCHUK:
4998c2ecf20Sopenharmony_ci		wmem = 0x05;
5008c2ecf20Sopenharmony_ci		break;
5018c2ecf20Sopenharmony_ci	default:
5028c2ecf20Sopenharmony_ci		wmem = 0x04;
5038c2ecf20Sopenharmony_ci		break;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem));
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/* requires the cmd-mutex to be held */
5108c2ecf20Sopenharmony_cistatic bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	int ret;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* read motion plus ID */
5158c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6);
5168c2ecf20Sopenharmony_ci	if (ret != 6)
5178c2ecf20Sopenharmony_ci		return false;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	hid_dbg(wdata->hdev, "motion plus ID: %6phC\n", rmem);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (rmem[5] == 0x05)
5228c2ecf20Sopenharmony_ci		return true;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	hid_info(wdata->hdev, "unknown motion plus ID: %6phC\n", rmem);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return false;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci/* requires the cmd-mutex to be held */
5308c2ecf20Sopenharmony_cistatic __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	int ret;
5338c2ecf20Sopenharmony_ci	__u8 rmem[6];
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* read motion plus ID */
5368c2ecf20Sopenharmony_ci	ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
5378c2ecf20Sopenharmony_ci	if (ret != 6)
5388c2ecf20Sopenharmony_ci		return WIIMOTE_MP_NONE;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	hid_dbg(wdata->hdev, "mapped motion plus ID: %6phC\n", rmem);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
5438c2ecf20Sopenharmony_ci	    rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
5448c2ecf20Sopenharmony_ci		return WIIMOTE_MP_NONE;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (rmem[4] == 0x04 && rmem[5] == 0x05)
5478c2ecf20Sopenharmony_ci		return WIIMOTE_MP_SINGLE;
5488c2ecf20Sopenharmony_ci	else if (rmem[4] == 0x05 && rmem[5] == 0x05)
5498c2ecf20Sopenharmony_ci		return WIIMOTE_MP_PASSTHROUGH_NUNCHUK;
5508c2ecf20Sopenharmony_ci	else if (rmem[4] == 0x07 && rmem[5] == 0x05)
5518c2ecf20Sopenharmony_ci		return WIIMOTE_MP_PASSTHROUGH_CLASSIC;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return WIIMOTE_MP_UNKNOWN;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/* device module handling */
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
5598c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_PENDING] = (const __u8[]){
5608c2ecf20Sopenharmony_ci		WIIMOD_NULL,
5618c2ecf20Sopenharmony_ci	},
5628c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_UNKNOWN] = (const __u8[]){
5638c2ecf20Sopenharmony_ci		WIIMOD_NO_MP,
5648c2ecf20Sopenharmony_ci		WIIMOD_NULL,
5658c2ecf20Sopenharmony_ci	},
5668c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_GENERIC] = (const __u8[]){
5678c2ecf20Sopenharmony_ci		WIIMOD_KEYS,
5688c2ecf20Sopenharmony_ci		WIIMOD_RUMBLE,
5698c2ecf20Sopenharmony_ci		WIIMOD_BATTERY,
5708c2ecf20Sopenharmony_ci		WIIMOD_LED1,
5718c2ecf20Sopenharmony_ci		WIIMOD_LED2,
5728c2ecf20Sopenharmony_ci		WIIMOD_LED3,
5738c2ecf20Sopenharmony_ci		WIIMOD_LED4,
5748c2ecf20Sopenharmony_ci		WIIMOD_ACCEL,
5758c2ecf20Sopenharmony_ci		WIIMOD_IR,
5768c2ecf20Sopenharmony_ci		WIIMOD_NULL,
5778c2ecf20Sopenharmony_ci	},
5788c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_GEN10] = (const __u8[]){
5798c2ecf20Sopenharmony_ci		WIIMOD_KEYS,
5808c2ecf20Sopenharmony_ci		WIIMOD_RUMBLE,
5818c2ecf20Sopenharmony_ci		WIIMOD_BATTERY,
5828c2ecf20Sopenharmony_ci		WIIMOD_LED1,
5838c2ecf20Sopenharmony_ci		WIIMOD_LED2,
5848c2ecf20Sopenharmony_ci		WIIMOD_LED3,
5858c2ecf20Sopenharmony_ci		WIIMOD_LED4,
5868c2ecf20Sopenharmony_ci		WIIMOD_ACCEL,
5878c2ecf20Sopenharmony_ci		WIIMOD_IR,
5888c2ecf20Sopenharmony_ci		WIIMOD_NULL,
5898c2ecf20Sopenharmony_ci	},
5908c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_GEN20] = (const __u8[]){
5918c2ecf20Sopenharmony_ci		WIIMOD_KEYS,
5928c2ecf20Sopenharmony_ci		WIIMOD_RUMBLE,
5938c2ecf20Sopenharmony_ci		WIIMOD_BATTERY,
5948c2ecf20Sopenharmony_ci		WIIMOD_LED1,
5958c2ecf20Sopenharmony_ci		WIIMOD_LED2,
5968c2ecf20Sopenharmony_ci		WIIMOD_LED3,
5978c2ecf20Sopenharmony_ci		WIIMOD_LED4,
5988c2ecf20Sopenharmony_ci		WIIMOD_ACCEL,
5998c2ecf20Sopenharmony_ci		WIIMOD_IR,
6008c2ecf20Sopenharmony_ci		WIIMOD_BUILTIN_MP,
6018c2ecf20Sopenharmony_ci		WIIMOD_NULL,
6028c2ecf20Sopenharmony_ci	},
6038c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) {
6048c2ecf20Sopenharmony_ci		WIIMOD_BATTERY,
6058c2ecf20Sopenharmony_ci		WIIMOD_LED1,
6068c2ecf20Sopenharmony_ci		WIIMOD_NO_MP,
6078c2ecf20Sopenharmony_ci		WIIMOD_NULL,
6088c2ecf20Sopenharmony_ci	},
6098c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_PRO_CONTROLLER] = (const __u8[]) {
6108c2ecf20Sopenharmony_ci		WIIMOD_BATTERY,
6118c2ecf20Sopenharmony_ci		WIIMOD_LED1,
6128c2ecf20Sopenharmony_ci		WIIMOD_LED2,
6138c2ecf20Sopenharmony_ci		WIIMOD_LED3,
6148c2ecf20Sopenharmony_ci		WIIMOD_LED4,
6158c2ecf20Sopenharmony_ci		WIIMOD_NO_MP,
6168c2ecf20Sopenharmony_ci		WIIMOD_NULL,
6178c2ecf20Sopenharmony_ci	},
6188c2ecf20Sopenharmony_ci};
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic void wiimote_modules_load(struct wiimote_data *wdata,
6218c2ecf20Sopenharmony_ci				 unsigned int devtype)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	bool need_input = false;
6248c2ecf20Sopenharmony_ci	const __u8 *mods, *iter;
6258c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
6268c2ecf20Sopenharmony_ci	int ret;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	mods = wiimote_devtype_mods[devtype];
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
6318c2ecf20Sopenharmony_ci		if (wiimod_table[*iter]->flags & WIIMOD_FLAG_INPUT) {
6328c2ecf20Sopenharmony_ci			need_input = true;
6338c2ecf20Sopenharmony_ci			break;
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if (need_input) {
6388c2ecf20Sopenharmony_ci		wdata->input = input_allocate_device();
6398c2ecf20Sopenharmony_ci		if (!wdata->input)
6408c2ecf20Sopenharmony_ci			return;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		input_set_drvdata(wdata->input, wdata);
6438c2ecf20Sopenharmony_ci		wdata->input->dev.parent = &wdata->hdev->dev;
6448c2ecf20Sopenharmony_ci		wdata->input->id.bustype = wdata->hdev->bus;
6458c2ecf20Sopenharmony_ci		wdata->input->id.vendor = wdata->hdev->vendor;
6468c2ecf20Sopenharmony_ci		wdata->input->id.product = wdata->hdev->product;
6478c2ecf20Sopenharmony_ci		wdata->input->id.version = wdata->hdev->version;
6488c2ecf20Sopenharmony_ci		wdata->input->name = WIIMOTE_NAME;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
6528c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
6538c2ecf20Sopenharmony_ci		if (!ops->probe)
6548c2ecf20Sopenharmony_ci			continue;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		ret = ops->probe(ops, wdata);
6578c2ecf20Sopenharmony_ci		if (ret)
6588c2ecf20Sopenharmony_ci			goto error;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (wdata->input) {
6628c2ecf20Sopenharmony_ci		ret = input_register_device(wdata->input);
6638c2ecf20Sopenharmony_ci		if (ret)
6648c2ecf20Sopenharmony_ci			goto error;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
6688c2ecf20Sopenharmony_ci	wdata->state.devtype = devtype;
6698c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
6708c2ecf20Sopenharmony_ci	return;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cierror:
6738c2ecf20Sopenharmony_ci	for ( ; iter-- != mods; ) {
6748c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
6758c2ecf20Sopenharmony_ci		if (ops->remove)
6768c2ecf20Sopenharmony_ci			ops->remove(ops, wdata);
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	if (wdata->input) {
6808c2ecf20Sopenharmony_ci		input_free_device(wdata->input);
6818c2ecf20Sopenharmony_ci		wdata->input = NULL;
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic void wiimote_modules_unload(struct wiimote_data *wdata)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	const __u8 *mods, *iter;
6888c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
6898c2ecf20Sopenharmony_ci	unsigned long flags;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	mods = wiimote_devtype_mods[wdata->state.devtype];
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
6948c2ecf20Sopenharmony_ci	wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
6958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* find end of list */
6988c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter)
6998c2ecf20Sopenharmony_ci		/* empty */ ;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (wdata->input) {
7028c2ecf20Sopenharmony_ci		input_get_device(wdata->input);
7038c2ecf20Sopenharmony_ci		input_unregister_device(wdata->input);
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	for ( ; iter-- != mods; ) {
7078c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
7088c2ecf20Sopenharmony_ci		if (ops->remove)
7098c2ecf20Sopenharmony_ci			ops->remove(ops, wdata);
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (wdata->input) {
7138c2ecf20Sopenharmony_ci		input_put_device(wdata->input);
7148c2ecf20Sopenharmony_ci		wdata->input = NULL;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci/* device extension handling */
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	unsigned long flags;
7238c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
7248c2ecf20Sopenharmony_ci	int ret;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	ops = wiimod_ext_table[ext];
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (ops->probe) {
7298c2ecf20Sopenharmony_ci		ret = ops->probe(ops, wdata);
7308c2ecf20Sopenharmony_ci		if (ret)
7318c2ecf20Sopenharmony_ci			ext = WIIMOTE_EXT_UNKNOWN;
7328c2ecf20Sopenharmony_ci	}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
7358c2ecf20Sopenharmony_ci	wdata->state.exttype = ext;
7368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistatic void wiimote_ext_unload(struct wiimote_data *wdata)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	unsigned long flags;
7428c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	ops = wiimod_ext_table[wdata->state.exttype];
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
7478c2ecf20Sopenharmony_ci	wdata->state.exttype = WIIMOTE_EXT_UNKNOWN;
7488c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
7498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (ops->remove)
7528c2ecf20Sopenharmony_ci		ops->remove(ops, wdata);
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic void wiimote_mp_load(struct wiimote_data *wdata)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	unsigned long flags;
7588c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
7598c2ecf20Sopenharmony_ci	int ret;
7608c2ecf20Sopenharmony_ci	__u8 mode = 2;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	ops = &wiimod_mp;
7638c2ecf20Sopenharmony_ci	if (ops->probe) {
7648c2ecf20Sopenharmony_ci		ret = ops->probe(ops, wdata);
7658c2ecf20Sopenharmony_ci		if (ret)
7668c2ecf20Sopenharmony_ci			mode = 1;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
7708c2ecf20Sopenharmony_ci	wdata->state.mp = mode;
7718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic void wiimote_mp_unload(struct wiimote_data *wdata)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	unsigned long flags;
7778c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (wdata->state.mp < 2)
7808c2ecf20Sopenharmony_ci		return;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	ops = &wiimod_mp;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
7858c2ecf20Sopenharmony_ci	wdata->state.mp = 0;
7868c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
7878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (ops->remove)
7908c2ecf20Sopenharmony_ci		ops->remove(ops, wdata);
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci/* device (re-)initialization and detection */
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
7968c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_PENDING] = "Pending",
7978c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_UNKNOWN] = "Unknown",
7988c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_GENERIC] = "Generic",
7998c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_GEN10] = "Nintendo Wii Remote (Gen 1)",
8008c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_GEN20] = "Nintendo Wii Remote Plus (Gen 2)",
8018c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_BALANCE_BOARD] = "Nintendo Wii Balance Board",
8028c2ecf20Sopenharmony_ci	[WIIMOTE_DEV_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
8038c2ecf20Sopenharmony_ci};
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci/* Try to guess the device type based on all collected information. We
8068c2ecf20Sopenharmony_ci * first try to detect by static extension types, then VID/PID and the
8078c2ecf20Sopenharmony_ci * device name. If we cannot detect the device, we use
8088c2ecf20Sopenharmony_ci * WIIMOTE_DEV_GENERIC so all modules will get probed on the device. */
8098c2ecf20Sopenharmony_cistatic void wiimote_init_set_type(struct wiimote_data *wdata,
8108c2ecf20Sopenharmony_ci				  __u8 exttype)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	__u8 devtype = WIIMOTE_DEV_GENERIC;
8138c2ecf20Sopenharmony_ci	__u16 vendor, product;
8148c2ecf20Sopenharmony_ci	const char *name;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	vendor = wdata->hdev->vendor;
8178c2ecf20Sopenharmony_ci	product = wdata->hdev->product;
8188c2ecf20Sopenharmony_ci	name = wdata->hdev->name;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (exttype == WIIMOTE_EXT_BALANCE_BOARD) {
8218c2ecf20Sopenharmony_ci		devtype = WIIMOTE_DEV_BALANCE_BOARD;
8228c2ecf20Sopenharmony_ci		goto done;
8238c2ecf20Sopenharmony_ci	} else if (exttype == WIIMOTE_EXT_PRO_CONTROLLER) {
8248c2ecf20Sopenharmony_ci		devtype = WIIMOTE_DEV_PRO_CONTROLLER;
8258c2ecf20Sopenharmony_ci		goto done;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (!strcmp(name, "Nintendo RVL-CNT-01")) {
8298c2ecf20Sopenharmony_ci		devtype = WIIMOTE_DEV_GEN10;
8308c2ecf20Sopenharmony_ci		goto done;
8318c2ecf20Sopenharmony_ci	} else if (!strcmp(name, "Nintendo RVL-CNT-01-TR")) {
8328c2ecf20Sopenharmony_ci		devtype = WIIMOTE_DEV_GEN20;
8338c2ecf20Sopenharmony_ci		goto done;
8348c2ecf20Sopenharmony_ci	} else if (!strcmp(name, "Nintendo RVL-WBC-01")) {
8358c2ecf20Sopenharmony_ci		devtype = WIIMOTE_DEV_BALANCE_BOARD;
8368c2ecf20Sopenharmony_ci		goto done;
8378c2ecf20Sopenharmony_ci	} else if (!strcmp(name, "Nintendo RVL-CNT-01-UC")) {
8388c2ecf20Sopenharmony_ci		devtype = WIIMOTE_DEV_PRO_CONTROLLER;
8398c2ecf20Sopenharmony_ci		goto done;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (vendor == USB_VENDOR_ID_NINTENDO) {
8438c2ecf20Sopenharmony_ci		if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) {
8448c2ecf20Sopenharmony_ci			devtype = WIIMOTE_DEV_GEN10;
8458c2ecf20Sopenharmony_ci			goto done;
8468c2ecf20Sopenharmony_ci		} else if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE2) {
8478c2ecf20Sopenharmony_ci			devtype = WIIMOTE_DEV_GEN20;
8488c2ecf20Sopenharmony_ci			goto done;
8498c2ecf20Sopenharmony_ci		}
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_cidone:
8538c2ecf20Sopenharmony_ci	if (devtype == WIIMOTE_DEV_GENERIC)
8548c2ecf20Sopenharmony_ci		hid_info(wdata->hdev, "cannot detect device; NAME: %s VID: %04x PID: %04x EXT: %04x\n",
8558c2ecf20Sopenharmony_ci			name, vendor, product, exttype);
8568c2ecf20Sopenharmony_ci	else
8578c2ecf20Sopenharmony_ci		hid_info(wdata->hdev, "detected device: %s\n",
8588c2ecf20Sopenharmony_ci			 wiimote_devtype_names[devtype]);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	wiimote_modules_load(wdata, devtype);
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic void wiimote_init_detect(struct wiimote_data *wdata)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	__u8 exttype = WIIMOTE_EXT_NONE, extdata[6];
8668c2ecf20Sopenharmony_ci	bool ext;
8678c2ecf20Sopenharmony_ci	int ret;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	wiimote_cmd_acquire_noint(wdata);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
8728c2ecf20Sopenharmony_ci	wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
8738c2ecf20Sopenharmony_ci	wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
8748c2ecf20Sopenharmony_ci	wiiproto_req_status(wdata);
8758c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	ret = wiimote_cmd_wait_noint(wdata);
8788c2ecf20Sopenharmony_ci	if (ret)
8798c2ecf20Sopenharmony_ci		goto out_release;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
8828c2ecf20Sopenharmony_ci	ext = wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED;
8838c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (!ext)
8868c2ecf20Sopenharmony_ci		goto out_release;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	wiimote_cmd_init_ext(wdata);
8898c2ecf20Sopenharmony_ci	exttype = wiimote_cmd_read_ext(wdata, extdata);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ciout_release:
8928c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
8938c2ecf20Sopenharmony_ci	wiimote_init_set_type(wdata, exttype);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	/* schedule MP timer */
8968c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
8978c2ecf20Sopenharmony_ci	if (!(wdata->state.flags & WIIPROTO_FLAG_BUILTIN_MP) &&
8988c2ecf20Sopenharmony_ci	    !(wdata->state.flags & WIIPROTO_FLAG_NO_MP))
8998c2ecf20Sopenharmony_ci		mod_timer(&wdata->timer, jiffies + HZ * 4);
9008c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci/*
9048c2ecf20Sopenharmony_ci * MP hotplug events are not generated by the wiimote. Therefore, we need
9058c2ecf20Sopenharmony_ci * polling to detect it. We use a 4s interval for polling MP registers. This
9068c2ecf20Sopenharmony_ci * seems reasonable considering applications can trigger it manually via
9078c2ecf20Sopenharmony_ci * sysfs requests.
9088c2ecf20Sopenharmony_ci */
9098c2ecf20Sopenharmony_cistatic void wiimote_init_poll_mp(struct wiimote_data *wdata)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	bool mp;
9128c2ecf20Sopenharmony_ci	__u8 mpdata[6];
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	wiimote_cmd_acquire_noint(wdata);
9158c2ecf20Sopenharmony_ci	wiimote_cmd_init_mp(wdata);
9168c2ecf20Sopenharmony_ci	mp = wiimote_cmd_read_mp(wdata, mpdata);
9178c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* load/unload MP module if it changed */
9208c2ecf20Sopenharmony_ci	if (mp) {
9218c2ecf20Sopenharmony_ci		if (!wdata->state.mp) {
9228c2ecf20Sopenharmony_ci			hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n");
9238c2ecf20Sopenharmony_ci			wiimote_mp_load(wdata);
9248c2ecf20Sopenharmony_ci		}
9258c2ecf20Sopenharmony_ci	} else if (wdata->state.mp) {
9268c2ecf20Sopenharmony_ci		wiimote_mp_unload(wdata);
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	mod_timer(&wdata->timer, jiffies + HZ * 4);
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci/*
9338c2ecf20Sopenharmony_ci * Check whether the wiimote is in the expected state. The extension registers
9348c2ecf20Sopenharmony_ci * may change during hotplug and initialization so we might get hotplug events
9358c2ecf20Sopenharmony_ci * that we caused by remapping some memory.
9368c2ecf20Sopenharmony_ci * We use some heuristics here to check known states. If the wiimote is in the
9378c2ecf20Sopenharmony_ci * expected state, we can ignore the hotplug event.
9388c2ecf20Sopenharmony_ci *
9398c2ecf20Sopenharmony_ci * Returns "true" if the device is in expected state, "false" if we should
9408c2ecf20Sopenharmony_ci * redo hotplug handling and extension initialization.
9418c2ecf20Sopenharmony_ci */
9428c2ecf20Sopenharmony_cistatic bool wiimote_init_check(struct wiimote_data *wdata)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	__u32 flags;
9458c2ecf20Sopenharmony_ci	__u8 type, data[6];
9468c2ecf20Sopenharmony_ci	bool ret, poll_mp;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
9498c2ecf20Sopenharmony_ci	flags = wdata->state.flags;
9508c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	wiimote_cmd_acquire_noint(wdata);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	/* If MP is used and active, but the extension is not, we expect:
9558c2ecf20Sopenharmony_ci	 *   read_mp_mapped() == WIIMOTE_MP_SINGLE
9568c2ecf20Sopenharmony_ci	 *   state.flags == !EXT_ACTIVE && !MP_PLUGGED && MP_ACTIVE
9578c2ecf20Sopenharmony_ci	 * We do not check EXT_PLUGGED because it might change during
9588c2ecf20Sopenharmony_ci	 * initialization of MP without extensions.
9598c2ecf20Sopenharmony_ci	 *  - If MP is unplugged/replugged, read_mp_mapped() fails
9608c2ecf20Sopenharmony_ci	 *  - If EXT is plugged, MP_PLUGGED will get set */
9618c2ecf20Sopenharmony_ci	if (wdata->state.exttype == WIIMOTE_EXT_NONE &&
9628c2ecf20Sopenharmony_ci	    wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) {
9638c2ecf20Sopenharmony_ci		type = wiimote_cmd_read_mp_mapped(wdata);
9648c2ecf20Sopenharmony_ci		ret = type == WIIMOTE_MP_SINGLE;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci		spin_lock_irq(&wdata->state.lock);
9678c2ecf20Sopenharmony_ci		ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
9688c2ecf20Sopenharmony_ci		ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED);
9698c2ecf20Sopenharmony_ci		ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
9708c2ecf20Sopenharmony_ci		spin_unlock_irq(&wdata->state.lock);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		if (!ret)
9738c2ecf20Sopenharmony_ci			hid_dbg(wdata->hdev, "state left: !EXT && MP\n");
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci		/* while MP is mapped, we get EXT_PLUGGED events */
9768c2ecf20Sopenharmony_ci		poll_mp = false;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci		goto out_release;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* If MP is unused, but the extension port is used, we expect:
9828c2ecf20Sopenharmony_ci	 *   read_ext == state.exttype
9838c2ecf20Sopenharmony_ci	 *   state.flags == !MP_ACTIVE && EXT_ACTIVE
9848c2ecf20Sopenharmony_ci	 * - If MP is plugged/unplugged, our timer detects it
9858c2ecf20Sopenharmony_ci	 * - If EXT is unplugged/replugged, EXT_ACTIVE will become unset */
9868c2ecf20Sopenharmony_ci	if (!(flags & WIIPROTO_FLAG_MP_USED) &&
9878c2ecf20Sopenharmony_ci	    wdata->state.exttype != WIIMOTE_EXT_NONE) {
9888c2ecf20Sopenharmony_ci		type = wiimote_cmd_read_ext(wdata, data);
9898c2ecf20Sopenharmony_ci		ret = type == wdata->state.exttype;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		spin_lock_irq(&wdata->state.lock);
9928c2ecf20Sopenharmony_ci		ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
9938c2ecf20Sopenharmony_ci		ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
9948c2ecf20Sopenharmony_ci		spin_unlock_irq(&wdata->state.lock);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci		if (!ret)
9978c2ecf20Sopenharmony_ci			hid_dbg(wdata->hdev, "state left: EXT && !MP\n");
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		/* poll MP for hotplug events */
10008c2ecf20Sopenharmony_ci		poll_mp = true;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		goto out_release;
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/* If neither MP nor an extension are used, we expect:
10068c2ecf20Sopenharmony_ci	 *   read_ext() == WIIMOTE_EXT_NONE
10078c2ecf20Sopenharmony_ci	 *   state.flags == !MP_ACTIVE && !EXT_ACTIVE && !EXT_PLUGGED
10088c2ecf20Sopenharmony_ci	 * No need to perform any action in this case as everything is
10098c2ecf20Sopenharmony_ci	 * disabled already.
10108c2ecf20Sopenharmony_ci	 * - If MP is plugged/unplugged, our timer detects it
10118c2ecf20Sopenharmony_ci	 * - If EXT is plugged, EXT_PLUGGED will be set */
10128c2ecf20Sopenharmony_ci	if (!(flags & WIIPROTO_FLAG_MP_USED) &&
10138c2ecf20Sopenharmony_ci	    wdata->state.exttype == WIIMOTE_EXT_NONE) {
10148c2ecf20Sopenharmony_ci		type = wiimote_cmd_read_ext(wdata, data);
10158c2ecf20Sopenharmony_ci		ret = type == wdata->state.exttype;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci		spin_lock_irq(&wdata->state.lock);
10188c2ecf20Sopenharmony_ci		ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
10198c2ecf20Sopenharmony_ci		ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
10208c2ecf20Sopenharmony_ci		ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED);
10218c2ecf20Sopenharmony_ci		spin_unlock_irq(&wdata->state.lock);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci		if (!ret)
10248c2ecf20Sopenharmony_ci			hid_dbg(wdata->hdev, "state left: !EXT && !MP\n");
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci		/* poll MP for hotplug events */
10278c2ecf20Sopenharmony_ci		poll_mp = true;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci		goto out_release;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	/* The trickiest part is if both EXT and MP are active. We cannot read
10338c2ecf20Sopenharmony_ci	 * the EXT ID, anymore, because MP is mapped over it. However, we use
10348c2ecf20Sopenharmony_ci	 * a handy trick here:
10358c2ecf20Sopenharmony_ci	 *   - EXT_ACTIVE is unset whenever !MP_PLUGGED is sent
10368c2ecf20Sopenharmony_ci	 * MP_PLUGGED might be re-sent again before we are scheduled, but
10378c2ecf20Sopenharmony_ci	 * EXT_ACTIVE will stay unset.
10388c2ecf20Sopenharmony_ci	 * So it is enough to check for mp_mapped() and MP_ACTIVE and
10398c2ecf20Sopenharmony_ci	 * EXT_ACTIVE. EXT_PLUGGED is a sanity check. */
10408c2ecf20Sopenharmony_ci	if (wdata->state.exttype != WIIMOTE_EXT_NONE &&
10418c2ecf20Sopenharmony_ci	    wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) {
10428c2ecf20Sopenharmony_ci		type = wiimote_cmd_read_mp_mapped(wdata);
10438c2ecf20Sopenharmony_ci		ret = type != WIIMOTE_MP_NONE;
10448c2ecf20Sopenharmony_ci		ret = ret && type != WIIMOTE_MP_UNKNOWN;
10458c2ecf20Sopenharmony_ci		ret = ret && type != WIIMOTE_MP_SINGLE;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci		spin_lock_irq(&wdata->state.lock);
10488c2ecf20Sopenharmony_ci		ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED);
10498c2ecf20Sopenharmony_ci		ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
10508c2ecf20Sopenharmony_ci		ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
10518c2ecf20Sopenharmony_ci		spin_unlock_irq(&wdata->state.lock);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci		if (!ret)
10548c2ecf20Sopenharmony_ci			hid_dbg(wdata->hdev, "state left: EXT && MP\n");
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci		/* while MP is mapped, we get EXT_PLUGGED events */
10578c2ecf20Sopenharmony_ci		poll_mp = false;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		goto out_release;
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	/* unknown state */
10638c2ecf20Sopenharmony_ci	ret = false;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ciout_release:
10668c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	/* only poll for MP if requested and if state didn't change */
10698c2ecf20Sopenharmony_ci	if (ret && poll_mp && !(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
10708c2ecf20Sopenharmony_ci	    !(flags & WIIPROTO_FLAG_NO_MP))
10718c2ecf20Sopenharmony_ci		wiimote_init_poll_mp(wdata);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	return ret;
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
10778c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_NONE] = "None",
10788c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_UNKNOWN] = "Unknown",
10798c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk",
10808c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
10818c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
10828c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
10838c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_DRUMS] = "Nintendo Wii Drums",
10848c2ecf20Sopenharmony_ci	[WIIMOTE_EXT_GUITAR] = "Nintendo Wii Guitar",
10858c2ecf20Sopenharmony_ci};
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci/*
10888c2ecf20Sopenharmony_ci * Handle hotplug events
10898c2ecf20Sopenharmony_ci * If we receive an hotplug event and the device-check failed, we deinitialize
10908c2ecf20Sopenharmony_ci * the extension ports, re-read all extension IDs and set the device into
10918c2ecf20Sopenharmony_ci * the desired state. This involves mapping MP into the main extension
10928c2ecf20Sopenharmony_ci * registers, setting up extension passthrough modes and initializing the
10938c2ecf20Sopenharmony_ci * requested extensions.
10948c2ecf20Sopenharmony_ci */
10958c2ecf20Sopenharmony_cistatic void wiimote_init_hotplug(struct wiimote_data *wdata)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	__u8 exttype, extdata[6], mpdata[6];
10988c2ecf20Sopenharmony_ci	__u32 flags;
10998c2ecf20Sopenharmony_ci	bool mp;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	hid_dbg(wdata->hdev, "detect extensions..\n");
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	wiimote_cmd_acquire_noint(wdata);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* get state snapshot that we will then work on */
11088c2ecf20Sopenharmony_ci	flags = wdata->state.flags;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	/* disable event forwarding temporarily */
11118c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
11128c2ecf20Sopenharmony_ci	wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	/* init extension and MP (deactivates current extension or MP) */
11178c2ecf20Sopenharmony_ci	wiimote_cmd_init_ext(wdata);
11188c2ecf20Sopenharmony_ci	if (flags & WIIPROTO_FLAG_NO_MP) {
11198c2ecf20Sopenharmony_ci		mp = false;
11208c2ecf20Sopenharmony_ci	} else {
11218c2ecf20Sopenharmony_ci		wiimote_cmd_init_mp(wdata);
11228c2ecf20Sopenharmony_ci		mp = wiimote_cmd_read_mp(wdata, mpdata);
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci	exttype = wiimote_cmd_read_ext(wdata, extdata);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	wiimote_cmd_release(wdata);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	/* load/unload extension module if it changed */
11298c2ecf20Sopenharmony_ci	if (exttype != wdata->state.exttype) {
11308c2ecf20Sopenharmony_ci		/* unload previous extension */
11318c2ecf20Sopenharmony_ci		wiimote_ext_unload(wdata);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci		if (exttype == WIIMOTE_EXT_UNKNOWN) {
11348c2ecf20Sopenharmony_ci			hid_info(wdata->hdev, "cannot detect extension; %6phC\n",
11358c2ecf20Sopenharmony_ci				 extdata);
11368c2ecf20Sopenharmony_ci		} else if (exttype == WIIMOTE_EXT_NONE) {
11378c2ecf20Sopenharmony_ci			spin_lock_irq(&wdata->state.lock);
11388c2ecf20Sopenharmony_ci			wdata->state.exttype = WIIMOTE_EXT_NONE;
11398c2ecf20Sopenharmony_ci			spin_unlock_irq(&wdata->state.lock);
11408c2ecf20Sopenharmony_ci		} else {
11418c2ecf20Sopenharmony_ci			hid_info(wdata->hdev, "detected extension: %s\n",
11428c2ecf20Sopenharmony_ci				 wiimote_exttype_names[exttype]);
11438c2ecf20Sopenharmony_ci			/* try loading new extension */
11448c2ecf20Sopenharmony_ci			wiimote_ext_load(wdata, exttype);
11458c2ecf20Sopenharmony_ci		}
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	/* load/unload MP module if it changed */
11498c2ecf20Sopenharmony_ci	if (mp) {
11508c2ecf20Sopenharmony_ci		if (!wdata->state.mp) {
11518c2ecf20Sopenharmony_ci			hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n");
11528c2ecf20Sopenharmony_ci			wiimote_mp_load(wdata);
11538c2ecf20Sopenharmony_ci		}
11548c2ecf20Sopenharmony_ci	} else if (wdata->state.mp) {
11558c2ecf20Sopenharmony_ci		wiimote_mp_unload(wdata);
11568c2ecf20Sopenharmony_ci	}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	/* if MP is not used, do not map or activate it */
11598c2ecf20Sopenharmony_ci	if (!(flags & WIIPROTO_FLAG_MP_USED))
11608c2ecf20Sopenharmony_ci		mp = false;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	/* map MP into main extension registers if used */
11638c2ecf20Sopenharmony_ci	if (mp) {
11648c2ecf20Sopenharmony_ci		wiimote_cmd_acquire_noint(wdata);
11658c2ecf20Sopenharmony_ci		wiimote_cmd_map_mp(wdata, exttype);
11668c2ecf20Sopenharmony_ci		wiimote_cmd_release(wdata);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci		/* delete MP hotplug timer */
11698c2ecf20Sopenharmony_ci		del_timer_sync(&wdata->timer);
11708c2ecf20Sopenharmony_ci	} else {
11718c2ecf20Sopenharmony_ci		/* reschedule MP hotplug timer */
11728c2ecf20Sopenharmony_ci		if (!(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
11738c2ecf20Sopenharmony_ci		    !(flags & WIIPROTO_FLAG_NO_MP))
11748c2ecf20Sopenharmony_ci			mod_timer(&wdata->timer, jiffies + HZ * 4);
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	spin_lock_irq(&wdata->state.lock);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	/* enable data forwarding again and set expected hotplug state */
11808c2ecf20Sopenharmony_ci	if (mp) {
11818c2ecf20Sopenharmony_ci		wdata->state.flags |= WIIPROTO_FLAG_MP_ACTIVE;
11828c2ecf20Sopenharmony_ci		if (wdata->state.exttype == WIIMOTE_EXT_NONE) {
11838c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
11848c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
11858c2ecf20Sopenharmony_ci		} else {
11868c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
11878c2ecf20Sopenharmony_ci			wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
11888c2ecf20Sopenharmony_ci			wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
11898c2ecf20Sopenharmony_ci		}
11908c2ecf20Sopenharmony_ci	} else if (wdata->state.exttype != WIIMOTE_EXT_NONE) {
11918c2ecf20Sopenharmony_ci		wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/* request status report for hotplug state updates */
11958c2ecf20Sopenharmony_ci	wiiproto_req_status(wdata);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	spin_unlock_irq(&wdata->state.lock);
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	hid_dbg(wdata->hdev, "detected extensions: MP: %d EXT: %d\n",
12008c2ecf20Sopenharmony_ci		wdata->state.mp, wdata->state.exttype);
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_cistatic void wiimote_init_worker(struct work_struct *work)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = container_of(work, struct wiimote_data,
12068c2ecf20Sopenharmony_ci						  init_worker);
12078c2ecf20Sopenharmony_ci	bool changed = false;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	if (wdata->state.devtype == WIIMOTE_DEV_PENDING) {
12108c2ecf20Sopenharmony_ci		wiimote_init_detect(wdata);
12118c2ecf20Sopenharmony_ci		changed = true;
12128c2ecf20Sopenharmony_ci	}
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	if (changed || !wiimote_init_check(wdata))
12158c2ecf20Sopenharmony_ci		wiimote_init_hotplug(wdata);
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	if (changed)
12188c2ecf20Sopenharmony_ci		kobject_uevent(&wdata->hdev->dev.kobj, KOBJ_CHANGE);
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_civoid __wiimote_schedule(struct wiimote_data *wdata)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING))
12248c2ecf20Sopenharmony_ci		schedule_work(&wdata->init_worker);
12258c2ecf20Sopenharmony_ci}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_cistatic void wiimote_schedule(struct wiimote_data *wdata)
12288c2ecf20Sopenharmony_ci{
12298c2ecf20Sopenharmony_ci	unsigned long flags;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
12328c2ecf20Sopenharmony_ci	__wiimote_schedule(wdata);
12338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic void wiimote_init_timeout(struct timer_list *t)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = from_timer(wdata, t, timer);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	wiimote_schedule(wdata);
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci/* protocol handlers */
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	const __u8 *iter, *mods;
12488c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	ops = wiimod_ext_table[wdata->state.exttype];
12518c2ecf20Sopenharmony_ci	if (ops->in_keys) {
12528c2ecf20Sopenharmony_ci		ops->in_keys(wdata, payload);
12538c2ecf20Sopenharmony_ci		return;
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	mods = wiimote_devtype_mods[wdata->state.devtype];
12578c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
12588c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
12598c2ecf20Sopenharmony_ci		if (ops->in_keys) {
12608c2ecf20Sopenharmony_ci			ops->in_keys(wdata, payload);
12618c2ecf20Sopenharmony_ci			break;
12628c2ecf20Sopenharmony_ci		}
12638c2ecf20Sopenharmony_ci	}
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_cistatic void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	const __u8 *iter, *mods;
12698c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	ops = wiimod_ext_table[wdata->state.exttype];
12728c2ecf20Sopenharmony_ci	if (ops->in_accel) {
12738c2ecf20Sopenharmony_ci		ops->in_accel(wdata, payload);
12748c2ecf20Sopenharmony_ci		return;
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	mods = wiimote_devtype_mods[wdata->state.devtype];
12788c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
12798c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
12808c2ecf20Sopenharmony_ci		if (ops->in_accel) {
12818c2ecf20Sopenharmony_ci			ops->in_accel(wdata, payload);
12828c2ecf20Sopenharmony_ci			break;
12838c2ecf20Sopenharmony_ci		}
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic bool valid_ext_handler(const struct wiimod_ops *ops, size_t len)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	if (!ops->in_ext)
12908c2ecf20Sopenharmony_ci		return false;
12918c2ecf20Sopenharmony_ci	if ((ops->flags & WIIMOD_FLAG_EXT8) && len < 8)
12928c2ecf20Sopenharmony_ci		return false;
12938c2ecf20Sopenharmony_ci	if ((ops->flags & WIIMOD_FLAG_EXT16) && len < 16)
12948c2ecf20Sopenharmony_ci		return false;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	return true;
12978c2ecf20Sopenharmony_ci}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_cistatic void handler_ext(struct wiimote_data *wdata, const __u8 *payload,
13008c2ecf20Sopenharmony_ci			size_t len)
13018c2ecf20Sopenharmony_ci{
13028c2ecf20Sopenharmony_ci	static const __u8 invalid[21] = { 0xff, 0xff, 0xff, 0xff,
13038c2ecf20Sopenharmony_ci					  0xff, 0xff, 0xff, 0xff,
13048c2ecf20Sopenharmony_ci					  0xff, 0xff, 0xff, 0xff,
13058c2ecf20Sopenharmony_ci					  0xff, 0xff, 0xff, 0xff,
13068c2ecf20Sopenharmony_ci					  0xff, 0xff, 0xff, 0xff,
13078c2ecf20Sopenharmony_ci					  0xff };
13088c2ecf20Sopenharmony_ci	const __u8 *iter, *mods;
13098c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
13108c2ecf20Sopenharmony_ci	bool is_mp;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	if (len > 21)
13138c2ecf20Sopenharmony_ci		len = 21;
13148c2ecf20Sopenharmony_ci	if (len < 6 || !memcmp(payload, invalid, len))
13158c2ecf20Sopenharmony_ci		return;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	/* if MP is active, track MP slot hotplugging */
13188c2ecf20Sopenharmony_ci	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
13198c2ecf20Sopenharmony_ci		/* this bit is set for invalid events (eg. during hotplug) */
13208c2ecf20Sopenharmony_ci		if (payload[5] & 0x01)
13218c2ecf20Sopenharmony_ci			return;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci		if (payload[4] & 0x01) {
13248c2ecf20Sopenharmony_ci			if (!(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED)) {
13258c2ecf20Sopenharmony_ci				hid_dbg(wdata->hdev, "MP hotplug: 1\n");
13268c2ecf20Sopenharmony_ci				wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
13278c2ecf20Sopenharmony_ci				__wiimote_schedule(wdata);
13288c2ecf20Sopenharmony_ci			}
13298c2ecf20Sopenharmony_ci		} else {
13308c2ecf20Sopenharmony_ci			if (wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED) {
13318c2ecf20Sopenharmony_ci				hid_dbg(wdata->hdev, "MP hotplug: 0\n");
13328c2ecf20Sopenharmony_ci				wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
13338c2ecf20Sopenharmony_ci				wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
13348c2ecf20Sopenharmony_ci				__wiimote_schedule(wdata);
13358c2ecf20Sopenharmony_ci			}
13368c2ecf20Sopenharmony_ci		}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci		/* detect MP data that is sent interleaved with EXT data */
13398c2ecf20Sopenharmony_ci		is_mp = payload[5] & 0x02;
13408c2ecf20Sopenharmony_ci	} else {
13418c2ecf20Sopenharmony_ci		is_mp = false;
13428c2ecf20Sopenharmony_ci	}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	/* ignore EXT events if no extension is active */
13458c2ecf20Sopenharmony_ci	if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE) && !is_mp)
13468c2ecf20Sopenharmony_ci		return;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/* try forwarding to extension handler, first */
13498c2ecf20Sopenharmony_ci	ops = wiimod_ext_table[wdata->state.exttype];
13508c2ecf20Sopenharmony_ci	if (is_mp && ops->in_mp) {
13518c2ecf20Sopenharmony_ci		ops->in_mp(wdata, payload);
13528c2ecf20Sopenharmony_ci		return;
13538c2ecf20Sopenharmony_ci	} else if (!is_mp && valid_ext_handler(ops, len)) {
13548c2ecf20Sopenharmony_ci		ops->in_ext(wdata, payload);
13558c2ecf20Sopenharmony_ci		return;
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	/* try forwarding to MP handler */
13598c2ecf20Sopenharmony_ci	ops = &wiimod_mp;
13608c2ecf20Sopenharmony_ci	if (is_mp && ops->in_mp) {
13618c2ecf20Sopenharmony_ci		ops->in_mp(wdata, payload);
13628c2ecf20Sopenharmony_ci		return;
13638c2ecf20Sopenharmony_ci	} else if (!is_mp && valid_ext_handler(ops, len)) {
13648c2ecf20Sopenharmony_ci		ops->in_ext(wdata, payload);
13658c2ecf20Sopenharmony_ci		return;
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	/* try forwarding to loaded modules */
13698c2ecf20Sopenharmony_ci	mods = wiimote_devtype_mods[wdata->state.devtype];
13708c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
13718c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
13728c2ecf20Sopenharmony_ci		if (is_mp && ops->in_mp) {
13738c2ecf20Sopenharmony_ci			ops->in_mp(wdata, payload);
13748c2ecf20Sopenharmony_ci			return;
13758c2ecf20Sopenharmony_ci		} else if (!is_mp && valid_ext_handler(ops, len)) {
13768c2ecf20Sopenharmony_ci			ops->in_ext(wdata, payload);
13778c2ecf20Sopenharmony_ci			return;
13788c2ecf20Sopenharmony_ci		}
13798c2ecf20Sopenharmony_ci	}
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci#define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0)
13838c2ecf20Sopenharmony_ci#define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1)
13848c2ecf20Sopenharmony_ci#define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2)
13858c2ecf20Sopenharmony_ci#define ir_to_input3(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 3)
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_cistatic void handler_ir(struct wiimote_data *wdata, const __u8 *payload,
13888c2ecf20Sopenharmony_ci		       bool packed, unsigned int id)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	const __u8 *iter, *mods;
13918c2ecf20Sopenharmony_ci	const struct wiimod_ops *ops;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	ops = wiimod_ext_table[wdata->state.exttype];
13948c2ecf20Sopenharmony_ci	if (ops->in_ir) {
13958c2ecf20Sopenharmony_ci		ops->in_ir(wdata, payload, packed, id);
13968c2ecf20Sopenharmony_ci		return;
13978c2ecf20Sopenharmony_ci	}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	mods = wiimote_devtype_mods[wdata->state.devtype];
14008c2ecf20Sopenharmony_ci	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
14018c2ecf20Sopenharmony_ci		ops = wiimod_table[*iter];
14028c2ecf20Sopenharmony_ci		if (ops->in_ir) {
14038c2ecf20Sopenharmony_ci			ops->in_ir(wdata, payload, packed, id);
14048c2ecf20Sopenharmony_ci			break;
14058c2ecf20Sopenharmony_ci		}
14068c2ecf20Sopenharmony_ci	}
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci/* reduced status report with "BB BB" key data only */
14108c2ecf20Sopenharmony_cistatic void handler_status_K(struct wiimote_data *wdata,
14118c2ecf20Sopenharmony_ci			     const __u8 *payload)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	/* on status reports the drm is reset so we need to resend the drm */
14168c2ecf20Sopenharmony_ci	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
14178c2ecf20Sopenharmony_ci}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci/* extended status report with "BB BB LF 00 00 VV" data */
14208c2ecf20Sopenharmony_cistatic void handler_status(struct wiimote_data *wdata, const __u8 *payload)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	handler_status_K(wdata, payload);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	/* update extension status */
14258c2ecf20Sopenharmony_ci	if (payload[2] & 0x02) {
14268c2ecf20Sopenharmony_ci		if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED)) {
14278c2ecf20Sopenharmony_ci			hid_dbg(wdata->hdev, "EXT hotplug: 1\n");
14288c2ecf20Sopenharmony_ci			wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
14298c2ecf20Sopenharmony_ci			__wiimote_schedule(wdata);
14308c2ecf20Sopenharmony_ci		}
14318c2ecf20Sopenharmony_ci	} else {
14328c2ecf20Sopenharmony_ci		if (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED) {
14338c2ecf20Sopenharmony_ci			hid_dbg(wdata->hdev, "EXT hotplug: 0\n");
14348c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
14358c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
14368c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
14378c2ecf20Sopenharmony_ci			wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
14388c2ecf20Sopenharmony_ci			__wiimote_schedule(wdata);
14398c2ecf20Sopenharmony_ci		}
14408c2ecf20Sopenharmony_ci	}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	wdata->state.cmd_battery = payload[5];
14438c2ecf20Sopenharmony_ci	if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0))
14448c2ecf20Sopenharmony_ci		wiimote_cmd_complete(wdata);
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci/* reduced generic report with "BB BB" key data only */
14488c2ecf20Sopenharmony_cistatic void handler_generic_K(struct wiimote_data *wdata, const __u8 *payload)
14498c2ecf20Sopenharmony_ci{
14508c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic void handler_data(struct wiimote_data *wdata, const __u8 *payload)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	__u16 offset = payload[3] << 8 | payload[4];
14568c2ecf20Sopenharmony_ci	__u8 size = (payload[2] >> 4) + 1;
14578c2ecf20Sopenharmony_ci	__u8 err = payload[2] & 0x0f;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_RMEM, offset)) {
14628c2ecf20Sopenharmony_ci		if (err)
14638c2ecf20Sopenharmony_ci			size = 0;
14648c2ecf20Sopenharmony_ci		else if (size > wdata->state.cmd_read_size)
14658c2ecf20Sopenharmony_ci			size = wdata->state.cmd_read_size;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci		wdata->state.cmd_read_size = size;
14688c2ecf20Sopenharmony_ci		if (wdata->state.cmd_read_buf)
14698c2ecf20Sopenharmony_ci			memcpy(wdata->state.cmd_read_buf, &payload[5], size);
14708c2ecf20Sopenharmony_ci		wiimote_cmd_complete(wdata);
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_cistatic void handler_return(struct wiimote_data *wdata, const __u8 *payload)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	__u8 err = payload[3];
14778c2ecf20Sopenharmony_ci	__u8 cmd = payload[2];
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	if (wiimote_cmd_pending(wdata, cmd, 0)) {
14828c2ecf20Sopenharmony_ci		wdata->state.cmd_err = err;
14838c2ecf20Sopenharmony_ci		wiimote_cmd_complete(wdata);
14848c2ecf20Sopenharmony_ci	} else if (err) {
14858c2ecf20Sopenharmony_ci		hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
14868c2ecf20Sopenharmony_ci									cmd);
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_cistatic void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
14918c2ecf20Sopenharmony_ci{
14928c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
14938c2ecf20Sopenharmony_ci	handler_accel(wdata, payload);
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
14998c2ecf20Sopenharmony_ci	handler_ext(wdata, &payload[2], 8);
15008c2ecf20Sopenharmony_ci}
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_cistatic void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15058c2ecf20Sopenharmony_ci	handler_accel(wdata, payload);
15068c2ecf20Sopenharmony_ci	ir_to_input0(wdata, &payload[5], false);
15078c2ecf20Sopenharmony_ci	ir_to_input1(wdata, &payload[8], false);
15088c2ecf20Sopenharmony_ci	ir_to_input2(wdata, &payload[11], false);
15098c2ecf20Sopenharmony_ci	ir_to_input3(wdata, &payload[14], false);
15108c2ecf20Sopenharmony_ci}
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_cistatic void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
15138c2ecf20Sopenharmony_ci{
15148c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15158c2ecf20Sopenharmony_ci	handler_ext(wdata, &payload[2], 19);
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_cistatic void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15218c2ecf20Sopenharmony_ci	ir_to_input0(wdata, &payload[2], false);
15228c2ecf20Sopenharmony_ci	ir_to_input1(wdata, &payload[4], true);
15238c2ecf20Sopenharmony_ci	ir_to_input2(wdata, &payload[7], false);
15248c2ecf20Sopenharmony_ci	ir_to_input3(wdata, &payload[9], true);
15258c2ecf20Sopenharmony_ci	handler_ext(wdata, &payload[12], 9);
15268c2ecf20Sopenharmony_ci}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_cistatic void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
15298c2ecf20Sopenharmony_ci{
15308c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15318c2ecf20Sopenharmony_ci	handler_accel(wdata, payload);
15328c2ecf20Sopenharmony_ci	handler_ext(wdata, &payload[5], 16);
15338c2ecf20Sopenharmony_ci}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_cistatic void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
15368c2ecf20Sopenharmony_ci{
15378c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15388c2ecf20Sopenharmony_ci	handler_accel(wdata, payload);
15398c2ecf20Sopenharmony_ci	ir_to_input0(wdata, &payload[5], false);
15408c2ecf20Sopenharmony_ci	ir_to_input1(wdata, &payload[7], true);
15418c2ecf20Sopenharmony_ci	ir_to_input2(wdata, &payload[10], false);
15428c2ecf20Sopenharmony_ci	ir_to_input3(wdata, &payload[12], true);
15438c2ecf20Sopenharmony_ci	handler_ext(wdata, &payload[15], 6);
15448c2ecf20Sopenharmony_ci}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_cistatic void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
15478c2ecf20Sopenharmony_ci{
15488c2ecf20Sopenharmony_ci	handler_ext(wdata, payload, 21);
15498c2ecf20Sopenharmony_ci}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_cistatic void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
15528c2ecf20Sopenharmony_ci{
15538c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	wdata->state.accel_split[0] = payload[2];
15568c2ecf20Sopenharmony_ci	wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
15578c2ecf20Sopenharmony_ci	wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	ir_to_input0(wdata, &payload[3], false);
15608c2ecf20Sopenharmony_ci	ir_to_input1(wdata, &payload[12], false);
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
15648c2ecf20Sopenharmony_ci{
15658c2ecf20Sopenharmony_ci	__u8 buf[5];
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	handler_keys(wdata, payload);
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02);
15708c2ecf20Sopenharmony_ci	wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	buf[0] = 0;
15738c2ecf20Sopenharmony_ci	buf[1] = 0;
15748c2ecf20Sopenharmony_ci	buf[2] = wdata->state.accel_split[0];
15758c2ecf20Sopenharmony_ci	buf[3] = payload[2];
15768c2ecf20Sopenharmony_ci	buf[4] = wdata->state.accel_split[1];
15778c2ecf20Sopenharmony_ci	handler_accel(wdata, buf);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	ir_to_input2(wdata, &payload[3], false);
15808c2ecf20Sopenharmony_ci	ir_to_input3(wdata, &payload[12], false);
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_cistruct wiiproto_handler {
15848c2ecf20Sopenharmony_ci	__u8 id;
15858c2ecf20Sopenharmony_ci	size_t size;
15868c2ecf20Sopenharmony_ci	void (*func)(struct wiimote_data *wdata, const __u8 *payload);
15878c2ecf20Sopenharmony_ci};
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_cistatic const struct wiiproto_handler handlers[] = {
15908c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status },
15918c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_STATUS, .size = 2, .func = handler_status_K },
15928c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data },
15938c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DATA, .size = 2, .func = handler_generic_K },
15948c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return },
15958c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_RETURN, .size = 2, .func = handler_generic_K },
15968c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
15978c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
15988c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KA, .size = 2, .func = handler_generic_K },
15998c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE },
16008c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KE, .size = 2, .func = handler_generic_K },
16018c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
16028c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KAI, .size = 2, .func = handler_generic_K },
16038c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE },
16048c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KEE, .size = 2, .func = handler_generic_K },
16058c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
16068c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KAE, .size = 2, .func = handler_generic_K },
16078c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
16088c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KIE, .size = 2, .func = handler_generic_K },
16098c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
16108c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_KAIE, .size = 2, .func = handler_generic_K },
16118c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E },
16128c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
16138c2ecf20Sopenharmony_ci	{ .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
16148c2ecf20Sopenharmony_ci	{ .id = 0 }
16158c2ecf20Sopenharmony_ci};
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_cistatic int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
16188c2ecf20Sopenharmony_ci							u8 *raw_data, int size)
16198c2ecf20Sopenharmony_ci{
16208c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = hid_get_drvdata(hdev);
16218c2ecf20Sopenharmony_ci	const struct wiiproto_handler *h;
16228c2ecf20Sopenharmony_ci	int i;
16238c2ecf20Sopenharmony_ci	unsigned long flags;
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	if (size < 1)
16268c2ecf20Sopenharmony_ci		return -EINVAL;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	for (i = 0; handlers[i].id; ++i) {
16298c2ecf20Sopenharmony_ci		h = &handlers[i];
16308c2ecf20Sopenharmony_ci		if (h->id == raw_data[0] && h->size < size) {
16318c2ecf20Sopenharmony_ci			spin_lock_irqsave(&wdata->state.lock, flags);
16328c2ecf20Sopenharmony_ci			h->func(wdata, &raw_data[1]);
16338c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&wdata->state.lock, flags);
16348c2ecf20Sopenharmony_ci			break;
16358c2ecf20Sopenharmony_ci		}
16368c2ecf20Sopenharmony_ci	}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	if (!handlers[i].id)
16398c2ecf20Sopenharmony_ci		hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0],
16408c2ecf20Sopenharmony_ci									size);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	return 0;
16438c2ecf20Sopenharmony_ci}
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_cistatic ssize_t wiimote_ext_show(struct device *dev,
16468c2ecf20Sopenharmony_ci				struct device_attribute *attr,
16478c2ecf20Sopenharmony_ci				char *buf)
16488c2ecf20Sopenharmony_ci{
16498c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
16508c2ecf20Sopenharmony_ci	__u8 type;
16518c2ecf20Sopenharmony_ci	unsigned long flags;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
16548c2ecf20Sopenharmony_ci	type = wdata->state.exttype;
16558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	switch (type) {
16588c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_NONE:
16598c2ecf20Sopenharmony_ci		return sprintf(buf, "none\n");
16608c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_NUNCHUK:
16618c2ecf20Sopenharmony_ci		return sprintf(buf, "nunchuk\n");
16628c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_CLASSIC_CONTROLLER:
16638c2ecf20Sopenharmony_ci		return sprintf(buf, "classic\n");
16648c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_BALANCE_BOARD:
16658c2ecf20Sopenharmony_ci		return sprintf(buf, "balanceboard\n");
16668c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_PRO_CONTROLLER:
16678c2ecf20Sopenharmony_ci		return sprintf(buf, "procontroller\n");
16688c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_DRUMS:
16698c2ecf20Sopenharmony_ci		return sprintf(buf, "drums\n");
16708c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_GUITAR:
16718c2ecf20Sopenharmony_ci		return sprintf(buf, "guitar\n");
16728c2ecf20Sopenharmony_ci	case WIIMOTE_EXT_UNKNOWN:
16738c2ecf20Sopenharmony_ci	default:
16748c2ecf20Sopenharmony_ci		return sprintf(buf, "unknown\n");
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_cistatic ssize_t wiimote_ext_store(struct device *dev,
16798c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
16808c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
16818c2ecf20Sopenharmony_ci{
16828c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	if (!strcmp(buf, "scan")) {
16858c2ecf20Sopenharmony_ci		wiimote_schedule(wdata);
16868c2ecf20Sopenharmony_ci	} else {
16878c2ecf20Sopenharmony_ci		return -EINVAL;
16888c2ecf20Sopenharmony_ci	}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	return strnlen(buf, PAGE_SIZE);
16918c2ecf20Sopenharmony_ci}
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(extension, S_IRUGO | S_IWUSR | S_IWGRP, wiimote_ext_show,
16948c2ecf20Sopenharmony_ci		   wiimote_ext_store);
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_cistatic ssize_t wiimote_dev_show(struct device *dev,
16978c2ecf20Sopenharmony_ci				struct device_attribute *attr,
16988c2ecf20Sopenharmony_ci				char *buf)
16998c2ecf20Sopenharmony_ci{
17008c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = dev_to_wii(dev);
17018c2ecf20Sopenharmony_ci	__u8 type;
17028c2ecf20Sopenharmony_ci	unsigned long flags;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
17058c2ecf20Sopenharmony_ci	type = wdata->state.devtype;
17068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	switch (type) {
17098c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_GENERIC:
17108c2ecf20Sopenharmony_ci		return sprintf(buf, "generic\n");
17118c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_GEN10:
17128c2ecf20Sopenharmony_ci		return sprintf(buf, "gen10\n");
17138c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_GEN20:
17148c2ecf20Sopenharmony_ci		return sprintf(buf, "gen20\n");
17158c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_BALANCE_BOARD:
17168c2ecf20Sopenharmony_ci		return sprintf(buf, "balanceboard\n");
17178c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_PRO_CONTROLLER:
17188c2ecf20Sopenharmony_ci		return sprintf(buf, "procontroller\n");
17198c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_PENDING:
17208c2ecf20Sopenharmony_ci		return sprintf(buf, "pending\n");
17218c2ecf20Sopenharmony_ci	case WIIMOTE_DEV_UNKNOWN:
17228c2ecf20Sopenharmony_ci	default:
17238c2ecf20Sopenharmony_ci		return sprintf(buf, "unknown\n");
17248c2ecf20Sopenharmony_ci	}
17258c2ecf20Sopenharmony_ci}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_cistatic DEVICE_ATTR(devtype, S_IRUGO, wiimote_dev_show, NULL);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_cistatic struct wiimote_data *wiimote_create(struct hid_device *hdev)
17308c2ecf20Sopenharmony_ci{
17318c2ecf20Sopenharmony_ci	struct wiimote_data *wdata;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
17348c2ecf20Sopenharmony_ci	if (!wdata)
17358c2ecf20Sopenharmony_ci		return NULL;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	wdata->hdev = hdev;
17388c2ecf20Sopenharmony_ci	hid_set_drvdata(hdev, wdata);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	spin_lock_init(&wdata->queue.lock);
17418c2ecf20Sopenharmony_ci	INIT_WORK(&wdata->queue.worker, wiimote_queue_worker);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	spin_lock_init(&wdata->state.lock);
17448c2ecf20Sopenharmony_ci	init_completion(&wdata->state.ready);
17458c2ecf20Sopenharmony_ci	mutex_init(&wdata->state.sync);
17468c2ecf20Sopenharmony_ci	wdata->state.drm = WIIPROTO_REQ_DRM_K;
17478c2ecf20Sopenharmony_ci	wdata->state.cmd_battery = 0xff;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	INIT_WORK(&wdata->init_worker, wiimote_init_worker);
17508c2ecf20Sopenharmony_ci	timer_setup(&wdata->timer, wiimote_init_timeout, 0);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	return wdata;
17538c2ecf20Sopenharmony_ci}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_cistatic void wiimote_destroy(struct wiimote_data *wdata)
17568c2ecf20Sopenharmony_ci{
17578c2ecf20Sopenharmony_ci	unsigned long flags;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	wiidebug_deinit(wdata);
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	/* prevent init_worker from being scheduled again */
17628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wdata->state.lock, flags);
17638c2ecf20Sopenharmony_ci	wdata->state.flags |= WIIPROTO_FLAG_EXITING;
17648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wdata->state.lock, flags);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	cancel_work_sync(&wdata->init_worker);
17678c2ecf20Sopenharmony_ci	del_timer_sync(&wdata->timer);
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev, &dev_attr_devtype);
17708c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	wiimote_mp_unload(wdata);
17738c2ecf20Sopenharmony_ci	wiimote_ext_unload(wdata);
17748c2ecf20Sopenharmony_ci	wiimote_modules_unload(wdata);
17758c2ecf20Sopenharmony_ci	cancel_work_sync(&wdata->queue.worker);
17768c2ecf20Sopenharmony_ci	hid_hw_close(wdata->hdev);
17778c2ecf20Sopenharmony_ci	hid_hw_stop(wdata->hdev);
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	kfree(wdata);
17808c2ecf20Sopenharmony_ci}
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_cistatic int wiimote_hid_probe(struct hid_device *hdev,
17838c2ecf20Sopenharmony_ci				const struct hid_device_id *id)
17848c2ecf20Sopenharmony_ci{
17858c2ecf20Sopenharmony_ci	struct wiimote_data *wdata;
17868c2ecf20Sopenharmony_ci	int ret;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	wdata = wiimote_create(hdev);
17918c2ecf20Sopenharmony_ci	if (!wdata) {
17928c2ecf20Sopenharmony_ci		hid_err(hdev, "Can't alloc device\n");
17938c2ecf20Sopenharmony_ci		return -ENOMEM;
17948c2ecf20Sopenharmony_ci	}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	ret = hid_parse(hdev);
17978c2ecf20Sopenharmony_ci	if (ret) {
17988c2ecf20Sopenharmony_ci		hid_err(hdev, "HID parse failed\n");
17998c2ecf20Sopenharmony_ci		goto err;
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
18038c2ecf20Sopenharmony_ci	if (ret) {
18048c2ecf20Sopenharmony_ci		hid_err(hdev, "HW start failed\n");
18058c2ecf20Sopenharmony_ci		goto err;
18068c2ecf20Sopenharmony_ci	}
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	ret = hid_hw_open(hdev);
18098c2ecf20Sopenharmony_ci	if (ret) {
18108c2ecf20Sopenharmony_ci		hid_err(hdev, "cannot start hardware I/O\n");
18118c2ecf20Sopenharmony_ci		goto err_stop;
18128c2ecf20Sopenharmony_ci	}
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	ret = device_create_file(&hdev->dev, &dev_attr_extension);
18158c2ecf20Sopenharmony_ci	if (ret) {
18168c2ecf20Sopenharmony_ci		hid_err(hdev, "cannot create sysfs attribute\n");
18178c2ecf20Sopenharmony_ci		goto err_close;
18188c2ecf20Sopenharmony_ci	}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	ret = device_create_file(&hdev->dev, &dev_attr_devtype);
18218c2ecf20Sopenharmony_ci	if (ret) {
18228c2ecf20Sopenharmony_ci		hid_err(hdev, "cannot create sysfs attribute\n");
18238c2ecf20Sopenharmony_ci		goto err_ext;
18248c2ecf20Sopenharmony_ci	}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	ret = wiidebug_init(wdata);
18278c2ecf20Sopenharmony_ci	if (ret)
18288c2ecf20Sopenharmony_ci		goto err_free;
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	hid_info(hdev, "New device registered\n");
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	/* schedule device detection */
18338c2ecf20Sopenharmony_ci	wiimote_schedule(wdata);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	return 0;
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_cierr_free:
18388c2ecf20Sopenharmony_ci	wiimote_destroy(wdata);
18398c2ecf20Sopenharmony_ci	return ret;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_cierr_ext:
18428c2ecf20Sopenharmony_ci	device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
18438c2ecf20Sopenharmony_cierr_close:
18448c2ecf20Sopenharmony_ci	hid_hw_close(hdev);
18458c2ecf20Sopenharmony_cierr_stop:
18468c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
18478c2ecf20Sopenharmony_cierr:
18488c2ecf20Sopenharmony_ci	input_free_device(wdata->ir);
18498c2ecf20Sopenharmony_ci	input_free_device(wdata->accel);
18508c2ecf20Sopenharmony_ci	kfree(wdata);
18518c2ecf20Sopenharmony_ci	return ret;
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_cistatic void wiimote_hid_remove(struct hid_device *hdev)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	struct wiimote_data *wdata = hid_get_drvdata(hdev);
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci	hid_info(hdev, "Device removed\n");
18598c2ecf20Sopenharmony_ci	wiimote_destroy(wdata);
18608c2ecf20Sopenharmony_ci}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_cistatic const struct hid_device_id wiimote_hid_devices[] = {
18638c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
18648c2ecf20Sopenharmony_ci				USB_DEVICE_ID_NINTENDO_WIIMOTE) },
18658c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
18668c2ecf20Sopenharmony_ci				USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
18678c2ecf20Sopenharmony_ci	{ }
18688c2ecf20Sopenharmony_ci};
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cibool wiimote_dpad_as_analog = false;
18718c2ecf20Sopenharmony_cimodule_param_named(dpad_as_analog, wiimote_dpad_as_analog, bool, 0644);
18728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dpad_as_analog, "Use D-Pad as main analog input");
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_cistatic struct hid_driver wiimote_hid_driver = {
18778c2ecf20Sopenharmony_ci	.name = "wiimote",
18788c2ecf20Sopenharmony_ci	.id_table = wiimote_hid_devices,
18798c2ecf20Sopenharmony_ci	.probe = wiimote_hid_probe,
18808c2ecf20Sopenharmony_ci	.remove = wiimote_hid_remove,
18818c2ecf20Sopenharmony_ci	.raw_event = wiimote_hid_event,
18828c2ecf20Sopenharmony_ci};
18838c2ecf20Sopenharmony_cimodule_hid_driver(wiimote_hid_driver);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
18868c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
18878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Nintendo Wii / Wii U peripherals");
1888