/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
/*
 * Sony PS4 DualShock 4 driver
 * https://eleccelerator.com/wiki/index.php?title=DualShock_4
 * https://gist.github.com/johndrinkwater/7708901
 * https://www.psdevwiki.com/ps4/DS4-USB
 */

#include "opt_hid.h"

#include <sys/param.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/stat.h>
#include <sys/sx.h>
#include <sys/sysctl.h>

#include <dev/evdev/input.h>
#include <dev/evdev/evdev.h>

#define	HID_DEBUG_VAR	ps4dshock_debug
#include <dev/hid/hgame.h>
#include <dev/hid/hid.h>
#include <dev/hid/hidbus.h>
#include <dev/hid/hidquirk.h>
#include <dev/hid/hidmap.h>
#include "usbdevs.h"

#ifdef HID_DEBUG
static int ps4dshock_debug = 1;

static SYSCTL_NODE(_hw_hid, OID_AUTO, ps4dshock, CTLFLAG_RW, 0,
		"Sony PS4 DualShock Gamepad");
SYSCTL_INT(_hw_hid_ps4dshock, OID_AUTO, debug, CTLFLAG_RWTUN,
		&ps4dshock_debug, 0, "Debug level");
#endif

static const uint8_t	ps4dshock_rdesc[] = {
	0x05, 0x01,		/* Usage Page (Generic Desktop Ctrls)	*/
	0x09, 0x05,		/* Usage (Game Pad)			*/
	0xA1, 0x01,		/* Collection (Application)		*/
	0x85, 0x01,		/*   Report ID (1)			*/
	0x09, 0x30,		/*   Usage (X)				*/
	0x09, 0x31,		/*   Usage (Y)				*/
	0x09, 0x33,		/*   Usage (Rx)				*/
	0x09, 0x34,		/*   Usage (Ry)				*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x04,		/*   Report Count (4)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x09, 0x39,		/*   Usage (Hat switch)			*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x25, 0x07,		/*   Logical Maximum (7)		*/
	0x35, 0x00,		/*   Physical Minimum (0)		*/
	0x46, 0x3B, 0x01,	/*   Physical Maximum (315)		*/
	0x65, 0x14,		/*   Unit (System: English Rotation, Length: Centimeter) */
	0x75, 0x04,		/*   Report Size (4)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x42,		/*   Input (Data,Var,Abs,Null State)	*/
	0x65, 0x00,		/*   Unit (None)			*/
	0x45, 0x00,		/*   Physical Maximum (0)		*/
	0x05, 0x09,		/*   Usage Page (Button)		*/
	0x19, 0x01,		/*   Usage Minimum (0x01)		*/
	0x29, 0x0E,		/*   Usage Maximum (0x0E)		*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x25, 0x01,		/*   Logical Maximum (1)		*/
	0x75, 0x01,		/*   Report Size (1)			*/
	0x95, 0x0E,		/*   Report Count (14)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
	0x09, 0x20,		/*   Usage (0x20)			*/
	0x75, 0x06,		/*   Report Size (6)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x25, 0x3F,		/*   Logical Maximum (63)		*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*   Usage Page (Generic Desktop Ctrls)	*/
	0x09, 0x32,		/*   Usage (Z)				*/
	0x09, 0x35,		/*   Usage (Rz)				*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x02,		/*   Report Count (2)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0xC0,			/* End Collection			*/
	0x05, 0x01,		/* Usage Page (Generic Desktop Ctrls)	*/
	0x09, 0x08,		/* Usage (Multi-axis Controller)	*/
	0xA1, 0x01,		/* Collection (Application)		*/
	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x27, 0xFF, 0xFF, 0x00, 0x00,	/*   Logical Maximum (65534)	*/
	0x75, 0x10,		/*   Report Size (16)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x05, 0x06,		/*   Usage Page (Generic Dev Ctrls)	*/
	0x09, 0x20,		/*   Usage (Battery Strength)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*   Usage Page (Generic Desktop Ctrls)	*/
	0x19, 0x33,		/*   Usage Minimum (RX)			*/
	0x29, 0x35,		/*   Usage Maximum (RZ)			*/
	0x16, 0x00, 0x80,	/*   Logical Minimum (-32768)		*/
	0x26, 0xFF, 0x7F,	/*   Logical Maximum (32767)		*/
	0x75, 0x10,		/*   Report Size (16)			*/
	0x95, 0x03,		/*   Report Count (3)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x19, 0x30,		/*   Usage Minimum (X)			*/
	0x29, 0x32,		/*   Usage Maximum (Z)			*/
	0x16, 0x00, 0x80,	/*   Logical Minimum (-32768)		*/
	0x26, 0xFF, 0x7F,	/*   Logical Maximum (32767)		*/
	0x95, 0x03,		/*   Report Count (3)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x05,		/*   Report Count (5)			*/
	0x81, 0x03,		/*   Input (Const)			*/
	0xC0,			/* End Collection			*/
	0x05, 0x0C,		/* Usage Page (Consumer)		*/
	0x09, 0x05,		/* Usage (Headphone)			*/
	0xA1, 0x01,		/* Collection (Application)		*/
	0x75, 0x05,		/*   Report Size (5)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x03,		/*   Input (Const)			*/
	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
	0x09, 0x20,		/*   Usage (0x20)			*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x25, 0x01,		/*   Logical Maximum (1)		*/
	0x75, 0x01,		/*   Report Size (1)			*/
	0x95, 0x02,		/*   Report Count (2)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x75, 0x01,		/*   Report Size (1)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x03,		/*   Input (Const)			*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x02,		/*   Report Count (2)			*/
	0x81, 0x03,		/*   Input (Const)			*/
	0xC0,			/* End Collection			*/
	0x05, 0x0D,		/* Usage Page (Digitizer)		*/
	0x09, 0x05,		/* Usage (Touch Pad)			*/
	0xA1, 0x01,		/* Collection (Application)		*/
	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x25, 0x03,		/*   Logical Maximum (3)		*/
	0x75, 0x04,		/*   Report Size (4)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x75, 0x04,		/*   Report Size (4)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x03,		/*   Input (Data,Var,Abs)		*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x56,		/*   Usage (0x56)			*/
	0x55, 0x0C,		/*   Unit Exponent (-4)			*/
	0x66, 0x01, 0x10,	/*   Unit (System: SI Linear, Time: Seconds) */
	0x46, 0xCC, 0x06,	/*   Physical Maximum (1740)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*   Unit (None)			*/
	0x45, 0x00,		/*   Physical Maximum (0)		*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x22,		/*   Usage (Finger)			*/
	0xA1, 0x02,		/*   Collection (Logical)		*/
	0x09, 0x51,		/*     Usage (0x51)			*/
	0x25, 0x7F,		/*     Logical Maximum (127)		*/
	0x75, 0x07,		/*     Report Size (7)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x42,		/*     Usage (Tip Switch)		*/
	0x25, 0x01,		/*     Logical Maximum (1)		*/
	0x75, 0x01,		/*     Report Size (1)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*     Usage Page (Generic Desktop Ctrls) */
	0x09, 0x30,		/*     Usage (X)			*/
	0x55, 0x0E,		/*     Unit Exponent (-2)		*/
	0x65, 0x11,		/*     Unit (System: SI Linear, Length: Centimeter) */
	0x35, 0x00,		/*     Physical Minimum (0)		*/
	0x46, 0xB8, 0x01,	/*     Physical Maximum (440)		*/
	0x26, 0x80, 0x07,	/*     Logical Maximum (1920)		*/
	0x75, 0x0C,		/*     Report Size (12)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x31,		/*     Usage (Y)			*/
	0x46, 0xC0, 0x00,	/*     Physical Maximum (192)		*/
	0x26, 0xAE, 0x03,	/*     Logical Maximum (942)		*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*     Unit (None)			*/
	0x45, 0x00,		/*     Physical Maximum (0)		*/
	0xC0,			/*   End Collection			*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x22,		/*   Usage (Finger)			*/
	0xA1, 0x02,		/*   Collection (Logical)		*/
	0x09, 0x51,		/*     Usage (0x51)			*/
	0x25, 0x7F,		/*     Logical Maximum (127)		*/
	0x75, 0x07,		/*     Report Size (7)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x42,		/*     Usage (Tip Switch)		*/
	0x25, 0x01,		/*     Logical Maximum (1)		*/
	0x75, 0x01,		/*     Report Size (1)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*     Usage Page (Generic Desktop Ctrls) */
	0x09, 0x30,		/*     Usage (X)			*/
	0x55, 0x0E,		/*     Unit Exponent (-2)		*/
	0x65, 0x11,		/*     Unit (System: SI Linear, Length: Centimeter) */
	0x35, 0x00,		/*     Physical Minimum (0)		*/
	0x46, 0xB8, 0x01,	/*     Physical Maximum (440)		*/
	0x26, 0x80, 0x07,	/*     Logical Maximum (1920)		*/
	0x75, 0x0C,		/*     Report Size (12)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x31,		/*     Usage (Y)			*/
	0x46, 0xC0, 0x00,	/*     Physical Maximum (192)		*/
	0x26, 0xAE, 0x03,	/*     Logical Maximum (942)		*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*     Unit (None)			*/
	0x45, 0x00,		/*     Physical Maximum (0)		*/
	0xC0,			/*   End Collection			*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x56,		/*   Usage (0x56)			*/
	0x55, 0x0C,		/*   Unit Exponent (-4)			*/
	0x66, 0x01, 0x10,	/*   Unit (System: SI Linear, Time: Seconds) */
	0x46, 0xCC, 0x06,	/*   Physical Maximum (1740)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*   Unit (None)			*/
	0x45, 0x00,		/*   Physical Maximum (0)		*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x22,		/*   Usage (Finger)			*/
	0xA1, 0x02,		/*   Collection (Logical)		*/
	0x09, 0x51,		/*     Usage (0x51)			*/
	0x25, 0x7F,		/*     Logical Maximum (127)		*/
	0x75, 0x07,		/*     Report Size (7)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x42,		/*     Usage (Tip Switch)		*/
	0x25, 0x01,		/*     Logical Maximum (1)		*/
	0x75, 0x01,		/*     Report Size (1)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*     Usage Page (Generic Desktop Ctrls) */
	0x09, 0x30,		/*     Usage (X)			*/
	0x55, 0x0E,		/*     Unit Exponent (-2)		*/
	0x65, 0x11,		/*     Unit (System: SI Linear, Length: Centimeter) */
	0x35, 0x00,		/*     Physical Minimum (0)		*/
	0x46, 0xB8, 0x01,	/*     Physical Maximum (440)		*/
	0x26, 0x80, 0x07,	/*     Logical Maximum (1920)		*/
	0x75, 0x0C,		/*     Report Size (12)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x31,		/*     Usage (Y)			*/
	0x46, 0xC0, 0x00,	/*     Physical Maximum (192)		*/
	0x26, 0xAE, 0x03,	/*     Logical Maximum (942)		*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*     Unit (None)			*/
	0x45, 0x00,		/*     Physical Maximum (0)		*/
	0xC0,			/*   End Collection			*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x22,		/*   Usage (Finger)			*/
	0xA1, 0x02,		/*   Collection (Logical)		*/
	0x09, 0x51,		/*     Usage (0x51)			*/
	0x25, 0x7F,		/*     Logical Maximum (127)		*/
	0x75, 0x07,		/*     Report Size (7)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x42,		/*     Usage (Tip Switch)		*/
	0x25, 0x01,		/*     Logical Maximum (1)		*/
	0x75, 0x01,		/*     Report Size (1)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*     Usage Page (Generic Desktop Ctrls) */
	0x09, 0x30,		/*     Usage (X)			*/
	0x55, 0x0E,		/*     Unit Exponent (-2)		*/
	0x65, 0x11,		/*     Unit (System: SI Linear, Length: Centimeter) */
	0x35, 0x00,		/*     Physical Minimum (0)		*/
	0x46, 0xB8, 0x01,	/*     Physical Maximum (440)		*/
	0x26, 0x80, 0x07,	/*     Logical Maximum (1920)		*/
	0x75, 0x0C,		/*     Report Size (12)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x31,		/*     Usage (Y)			*/
	0x46, 0xC0, 0x00,	/*     Physical Maximum (192)		*/
	0x26, 0xAE, 0x03,	/*     Logical Maximum (942)		*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*     Unit (None)			*/
	0x45, 0x00,		/*     Physical Maximum (0)		*/
	0xC0,			/*   End Collection			*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x56,		/*   Usage (0x56)			*/
	0x55, 0x0C,		/*   Unit Exponent (-4)			*/
	0x66, 0x01, 0x10,	/*   Unit (System: SI Linear, Time: Seconds) */
	0x46, 0xCC, 0x06,	/*   Physical Maximum (1740)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0x81, 0x02,		/*   Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*   Unit (None)			*/
	0x45, 0x00,		/*   Physical Maximum (0)		*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x22,		/*   Usage (Finger)			*/
	0xA1, 0x02,		/*   Collection (Logical)		*/
	0x09, 0x51,		/*     Usage (0x51)			*/
	0x25, 0x7F,		/*     Logical Maximum (127)		*/
	0x75, 0x07,		/*     Report Size (7)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x42,		/*     Usage (Tip Switch)		*/
	0x25, 0x01,		/*     Logical Maximum (1)		*/
	0x75, 0x01,		/*     Report Size (1)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*     Usage Page (Generic Desktop Ctrls) */
	0x09, 0x30,		/*     Usage (X)			*/
	0x55, 0x0E,		/*     Unit Exponent (-2)		*/
	0x65, 0x11,		/*     Unit (System: SI Linear, Length: Centimeter) */
	0x35, 0x00,		/*     Physical Minimum (0)		*/
	0x46, 0xB8, 0x01,	/*     Physical Maximum (440)		*/
	0x26, 0x80, 0x07,	/*     Logical Maximum (1920)		*/
	0x75, 0x0C,		/*     Report Size (12)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x31,		/*     Usage (Y)			*/
	0x46, 0xC0, 0x00,	/*     Physical Maximum (192)		*/
	0x26, 0xAE, 0x03,	/*     Logical Maximum (942)		*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*     Unit (None)			*/
	0x45, 0x00,		/*     Physical Maximum (0)		*/
	0xC0,			/*   End Collection			*/
	0x05, 0x0D,		/*   Usage Page (Digitizer)		*/
	0x09, 0x22,		/*   Usage (Finger)			*/
	0xA1, 0x02,		/*   Collection (Logical)		*/
	0x09, 0x51,		/*     Usage (0x51)			*/
	0x25, 0x7F,		/*     Logical Maximum (127)		*/
	0x75, 0x07,		/*     Report Size (7)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x42,		/*     Usage (Tip Switch)		*/
	0x25, 0x01,		/*     Logical Maximum (1)		*/
	0x75, 0x01,		/*     Report Size (1)			*/
	0x95, 0x01,		/*     Report Count (1)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x05, 0x01,		/*     Usage Page (Generic Desktop Ctrls) */
	0x09, 0x30,		/*     Usage (X)			*/
	0x55, 0x0E,		/*     Unit Exponent (-2)		*/
	0x65, 0x11,		/*     Unit (System: SI Linear, Length: Centimeter) */
	0x35, 0x00,		/*     Physical Minimum (0)		*/
	0x46, 0xB8, 0x01,	/*     Physical Maximum (440)		*/
	0x26, 0x80, 0x07,	/*     Logical Maximum (1920)		*/
	0x75, 0x0C,		/*     Report Size (12)			*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x09, 0x31,		/*     Usage (Y)			*/
	0x46, 0xC0, 0x00,	/*     Physical Maximum (192)		*/
	0x26, 0xAE, 0x03,	/*     Logical Maximum (942)		*/
	0x81, 0x02,		/*     Input (Data,Var,Abs)		*/
	0x65, 0x00,		/*     Unit (None)			*/
	0x45, 0x00,		/*     Physical Maximum (0)		*/
	0xC0,			/*   End Collection			*/
	0x75, 0x08,		/*   Report Size (8)			*/
	0x95, 0x03,		/*   Report Count (3)			*/
	0x81, 0x03,		/*   Input (Const)			*/
	/* Output and feature reports */
	0x85, 0x05,		/*   Report ID (5)			*/
	0x06, 0x00, 0xFF,	/*   Usage Page (Vendor Defined 0xFF00)	*/
	0x09, 0x22,		/*   Usage (0x22)			*/
	0x15, 0x00,		/*   Logical Minimum (0)		*/
	0x26, 0xFF, 0x00,	/*   Logical Maximum (255)		*/
	0x95, 0x1F,		/*   Report Count (31)			*/
	0x91, 0x02,		/*   Output (Data,Var,Abs)		*/
	0x85, 0x04,		/*   Report ID (4)			*/
	0x09, 0x23,		/*   Usage (0x23)			*/
	0x95, 0x24,		/*   Report Count (36)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x02,		/*   Report ID (2)			*/
	0x09, 0x24,		/*   Usage (0x24)			*/
	0x95, 0x24,		/*   Report Count (36)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x08,		/*   Report ID (8)			*/
	0x09, 0x25,		/*   Usage (0x25)			*/
	0x95, 0x03,		/*   Report Count (3)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x10,		/*   Report ID (16)			*/
	0x09, 0x26,		/*   Usage (0x26)			*/
	0x95, 0x04,		/*   Report Count (4)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x11,		/*   Report ID (17)			*/
	0x09, 0x27,		/*   Usage (0x27)			*/
	0x95, 0x02,		/*   Report Count (2)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x12,		/*   Report ID (18)			*/
	0x06, 0x02, 0xFF,	/*   Usage Page (Vendor Defined 0xFF02)	*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x95, 0x0F,		/*   Report Count (15)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x13,		/*   Report ID (19)			*/
	0x09, 0x22,		/*   Usage (0x22)			*/
	0x95, 0x16,		/*   Report Count (22)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x14,		/*   Report ID (20)			*/
	0x06, 0x05, 0xFF,	/*   Usage Page (Vendor Defined 0xFF05)	*/
	0x09, 0x20,		/*   Usage (0x20)			*/
	0x95, 0x10,		/*   Report Count (16)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x15,		/*   Report ID (21)			*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x95, 0x2C,		/*   Report Count (44)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x06, 0x80, 0xFF,	/*   Usage Page (Vendor Defined 0xFF80)	*/
	0x85, 0x80,		/*   Report ID (-128)			*/
	0x09, 0x20,		/*   Usage (0x20)			*/
	0x95, 0x06,		/*   Report Count (6)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x81,		/*   Report ID (-127)			*/
	0x09, 0x21,		/*   Usage (0x21)			*/
	0x95, 0x06,		/*   Report Count (6)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x82,		/*   Report ID (-126)			*/
	0x09, 0x22,		/*   Usage (0x22)			*/
	0x95, 0x05,		/*   Report Count (5)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x83,		/*   Report ID (-125)			*/
	0x09, 0x23,		/*   Usage (0x23)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x84,		/*   Report ID (-124)			*/
	0x09, 0x24,		/*   Usage (0x24)			*/
	0x95, 0x04,		/*   Report Count (4)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x85,		/*   Report ID (-123)			*/
	0x09, 0x25,		/*   Usage (0x25)			*/
	0x95, 0x06,		/*   Report Count (6)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x86,		/*   Report ID (-122)			*/
	0x09, 0x26,		/*   Usage (0x26)			*/
	0x95, 0x06,		/*   Report Count (6)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x87,		/*   Report ID (-121)			*/
	0x09, 0x27,		/*   Usage (0x27)			*/
	0x95, 0x23,		/*   Report Count (35)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x88,		/*   Report ID (-120)			*/
	0x09, 0x28,		/*   Usage (0x28)			*/
	0x95, 0x22,		/*   Report Count (34)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x89,		/*   Report ID (-119)			*/
	0x09, 0x29,		/*   Usage (0x29)			*/
	0x95, 0x02,		/*   Report Count (2)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x90,		/*   Report ID (-112)			*/
	0x09, 0x30,		/*   Usage (0x30)			*/
	0x95, 0x05,		/*   Report Count (5)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x91,		/*   Report ID (-111)			*/
	0x09, 0x31,		/*   Usage (0x31)			*/
	0x95, 0x03,		/*   Report Count (3)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x92,		/*   Report ID (-110)			*/
	0x09, 0x32,		/*   Usage (0x32)			*/
	0x95, 0x03,		/*   Report Count (3)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0x93,		/*   Report ID (-109)			*/
	0x09, 0x33,		/*   Usage (0x33)			*/
	0x95, 0x0C,		/*   Report Count (12)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA0,		/*   Report ID (-96)			*/
	0x09, 0x40,		/*   Usage (0x40)			*/
	0x95, 0x06,		/*   Report Count (6)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA1,		/*   Report ID (-95)			*/
	0x09, 0x41,		/*   Usage (0x41)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA2,		/*   Report ID (-94)			*/
	0x09, 0x42,		/*   Usage (0x42)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA3,		/*   Report ID (-93)			*/
	0x09, 0x43,		/*   Usage (0x43)			*/
	0x95, 0x30,		/*   Report Count (48)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA4,		/*   Report ID (-92)			*/
	0x09, 0x44,		/*   Usage (0x44)			*/
	0x95, 0x0D,		/*   Report Count (13)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA5,		/*   Report ID (-91)			*/
	0x09, 0x45,		/*   Usage (0x45)			*/
	0x95, 0x15,		/*   Report Count (21)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA6,		/*   Report ID (-90)			*/
	0x09, 0x46,		/*   Usage (0x46)			*/
	0x95, 0x15,		/*   Report Count (21)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xF0,		/*   Report ID (-16)			*/
	0x09, 0x47,		/*   Usage (0x47)			*/
	0x95, 0x3F,		/*   Report Count (63)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xF1,		/*   Report ID (-15)			*/
	0x09, 0x48,		/*   Usage (0x48)			*/
	0x95, 0x3F,		/*   Report Count (63)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xF2,		/*   Report ID (-14)			*/
	0x09, 0x49,		/*   Usage (0x49)			*/
	0x95, 0x0F,		/*   Report Count (15)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA7,		/*   Report ID (-89)			*/
	0x09, 0x4A,		/*   Usage (0x4A)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA8,		/*   Report ID (-88)			*/
	0x09, 0x4B,		/*   Usage (0x4B)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xA9,		/*   Report ID (-87)			*/
	0x09, 0x4C,		/*   Usage (0x4C)			*/
	0x95, 0x08,		/*   Report Count (8)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xAA,		/*   Report ID (-86)			*/
	0x09, 0x4E,		/*   Usage (0x4E)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xAB,		/*   Report ID (-85)			*/
	0x09, 0x4F,		/*   Usage (0x4F)			*/
	0x95, 0x39,		/*   Report Count (57)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xAC,		/*   Report ID (-84)			*/
	0x09, 0x50,		/*   Usage (0x50)			*/
	0x95, 0x39,		/*   Report Count (57)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xAD,		/*   Report ID (-83)			*/
	0x09, 0x51,		/*   Usage (0x51)			*/
	0x95, 0x0B,		/*   Report Count (11)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xAE,		/*   Report ID (-82)			*/
	0x09, 0x52,		/*   Usage (0x52)			*/
	0x95, 0x01,		/*   Report Count (1)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xAF,		/*   Report ID (-81)			*/
	0x09, 0x53,		/*   Usage (0x53)			*/
	0x95, 0x02,		/*   Report Count (2)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0x85, 0xB0,		/*   Report ID (-80)			*/
	0x09, 0x54,		/*   Usage (0x54)			*/
	0x95, 0x3F,		/*   Report Count (63)			*/
	0xB1, 0x02,		/*   Feature (Data,Var,Abs)		*/
	0xC0,			/* End Collection			*/
};

#define	PS4DS_GYRO_RES_PER_DEG_S	1024
#define	PS4DS_ACC_RES_PER_G		8192
#define	PS4DS_MAX_TOUCHPAD_PACKETS	4
#define	PS4DS_FEATURE_REPORT2_SIZE	37
#define	PS4DS_OUTPUT_REPORT5_SIZE	32
#define	PS4DS_OUTPUT_REPORT11_SIZE	78

static hidmap_cb_t	ps4dshock_final_cb;
static hidmap_cb_t	ps4dsacc_data_cb;
static hidmap_cb_t	ps4dsacc_tstamp_cb;
static hidmap_cb_t	ps4dsacc_final_cb;
static hidmap_cb_t	ps4dsmtp_data_cb;
static hidmap_cb_t	ps4dsmtp_npackets_cb;
static hidmap_cb_t	ps4dsmtp_final_cb;

struct ps4ds_out5 {
	uint8_t features;
	uint8_t	reserved1;
	uint8_t	reserved2;
	uint8_t	rumble_right;
	uint8_t	rumble_left;
	uint8_t	led_color_r;
	uint8_t	led_color_g;
	uint8_t	led_color_b;
	uint8_t	led_delay_on;	/* centiseconds */
	uint8_t	led_delay_off;
} __attribute__((packed));

static const struct ps4ds_led {
	int	r;
	int	g;
	int	b;
} ps4ds_leds[] = {
	/* The first 4 entries match the PS4, other from Linux driver */
	{ 0x00, 0x00, 0x40 },	/* Blue   */
	{ 0x40, 0x00, 0x00 },	/* Red	  */
	{ 0x00, 0x40, 0x00 },	/* Green  */
	{ 0x20, 0x00, 0x20 },	/* Pink   */
	{ 0x02, 0x01, 0x00 },	/* Orange */
	{ 0x00, 0x01, 0x01 },	/* Teal   */
	{ 0x01, 0x01, 0x01 }	/* White  */
};

enum ps4ds_led_state {
	PS4DS_LED_OFF,
	PS4DS_LED_ON,
	PS4DS_LED_BLINKING,
	PD4DS_LED_CNT,
};

/* Map structure for accelerometer and gyro. */
struct ps4ds_calib_data {
	int32_t usage;
	int32_t code;
	int32_t res;
	int32_t range;
	/* Calibration data for accelerometer and gyro. */
	int16_t bias;
	int32_t sens_numer;
	int32_t sens_denom;
};

enum {
	PS4DS_TSTAMP,
	PS4DS_CID1,
	PS4DS_TIP1,
	PS4DS_X1,
	PS4DS_Y1,
	PS4DS_CID2,
	PS4DS_TIP2,
	PS4DS_X2,
	PS4DS_Y2,
	PS4DS_NTPUSAGES,
};

struct ps4dshock_softc {
	struct hidmap		hm;

	bool			is_bluetooth;

	struct sx		lock;
	enum ps4ds_led_state	led_state;
	struct ps4ds_led	led_color;
	int			led_delay_on;	/* msecs */
	int			led_delay_off;

	int			rumble_right;
	int			rumble_left;
};

struct ps4dsacc_softc {
	struct hidmap		hm;

	uint16_t		hw_tstamp;
	int32_t			ev_tstamp;

	struct ps4ds_calib_data	calib_data[6];
};

struct ps4dsmtp_softc {
	struct hidmap		hm;

	struct hid_location	btn_loc;
	u_int		npackets;
	int32_t		*data_ptr;
	int32_t		data[PS4DS_MAX_TOUCHPAD_PACKETS * PS4DS_NTPUSAGES];

	bool		do_tstamps;
	uint8_t		hw_tstamp;
	int32_t		ev_tstamp;
	bool		touch;
};

#define PD4DSHOCK_OFFSET(field) offsetof(struct ps4dshock_softc, field)
enum {
	PD4DSHOCK_SYSCTL_LED_STATE =	PD4DSHOCK_OFFSET(led_state),
	PD4DSHOCK_SYSCTL_LED_COLOR_R =	PD4DSHOCK_OFFSET(led_color.r),
	PD4DSHOCK_SYSCTL_LED_COLOR_G =	PD4DSHOCK_OFFSET(led_color.g),
	PD4DSHOCK_SYSCTL_LED_COLOR_B =	PD4DSHOCK_OFFSET(led_color.b),
	PD4DSHOCK_SYSCTL_LED_DELAY_ON =	PD4DSHOCK_OFFSET(led_delay_on),
	PD4DSHOCK_SYSCTL_LED_DELAY_OFF=	PD4DSHOCK_OFFSET(led_delay_off),
#define	PD4DSHOCK_SYSCTL_LAST		PD4DSHOCK_SYSCTL_LED_DELAY_OFF
};

#define PS4DS_MAP_BTN(number, code)		\
	{ HIDMAP_KEY(HUP_BUTTON, number, code) }
#define PS4DS_MAP_ABS(usage, code)		\
	{ HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) }
#define PS4DS_MAP_FLT(usage, code)		\
	{ HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), .flat = 15 }
#define PS4DS_MAP_VSW(usage, code)	\
	{ HIDMAP_SW(HUP_MICROSOFT, usage, code) }
#define PS4DS_MAP_GCB(usage, callback)	\
	{ HIDMAP_ANY_CB(HUP_GENERIC_DESKTOP, HUG_##usage, callback) }
#define PS4DS_MAP_VCB(usage, callback)	\
	{ HIDMAP_ANY_CB(HUP_MICROSOFT, usage, callback) }
#define PS4DS_FINALCB(cb)			\
	{ HIDMAP_FINAL_CB(&cb) }

static const struct hidmap_item ps4dshock_map[] = {
	PS4DS_MAP_FLT(X,		ABS_X),
	PS4DS_MAP_FLT(Y,		ABS_Y),
	PS4DS_MAP_ABS(Z,		ABS_Z),
	PS4DS_MAP_FLT(RX,		ABS_RX),
	PS4DS_MAP_FLT(RY,		ABS_RY),
	PS4DS_MAP_ABS(RZ,		ABS_RZ),
	PS4DS_MAP_BTN(1,		BTN_WEST),
	PS4DS_MAP_BTN(2,		BTN_SOUTH),
	PS4DS_MAP_BTN(3,		BTN_EAST),
	PS4DS_MAP_BTN(4,		BTN_NORTH),
	PS4DS_MAP_BTN(5,		BTN_TL),
	PS4DS_MAP_BTN(6,		BTN_TR),
	PS4DS_MAP_BTN(7,		BTN_TL2),
	PS4DS_MAP_BTN(8,		BTN_TR2),
	PS4DS_MAP_BTN(9,		BTN_SELECT),
	PS4DS_MAP_BTN(10,		BTN_START),
	PS4DS_MAP_BTN(11,		BTN_THUMBL),
	PS4DS_MAP_BTN(12,		BTN_THUMBR),
	PS4DS_MAP_BTN(13,		BTN_MODE),
	/* Click button is handled by touchpad driver */
	/* PS4DS_MAP_BTN(14,	BTN_LEFT), */
	PS4DS_MAP_GCB(HAT_SWITCH,	hgame_hat_switch_cb),
	PS4DS_FINALCB(			ps4dshock_final_cb),
};
static const struct hidmap_item ps4dsacc_map[] = {
	PS4DS_MAP_GCB(X,		ps4dsacc_data_cb),
	PS4DS_MAP_GCB(Y,		ps4dsacc_data_cb),
	PS4DS_MAP_GCB(Z,		ps4dsacc_data_cb),
	PS4DS_MAP_GCB(RX,		ps4dsacc_data_cb),
	PS4DS_MAP_GCB(RY,		ps4dsacc_data_cb),
	PS4DS_MAP_GCB(RZ,		ps4dsacc_data_cb),
	PS4DS_MAP_VCB(0x0021,		ps4dsacc_tstamp_cb),
	PS4DS_FINALCB(			ps4dsacc_final_cb),
};
static const struct hidmap_item ps4dshead_map[] = {
	PS4DS_MAP_VSW(0x0020,		SW_MICROPHONE_INSERT),
	PS4DS_MAP_VSW(0x0021,		SW_HEADPHONE_INSERT),
};
static const struct hidmap_item ps4dsmtp_map[] = {
	{ HIDMAP_ABS_CB(HUP_MICROSOFT, 0x0021, 		ps4dsmtp_npackets_cb)},
	{ HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_SCAN_TIME,	ps4dsmtp_data_cb) },
	{ HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_CONTACTID,	ps4dsmtp_data_cb) },
	{ HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_TIP_SWITCH,	ps4dsmtp_data_cb) },
	{ HIDMAP_ABS_CB(HUP_GENERIC_DESKTOP, HUG_X,	ps4dsmtp_data_cb) },
	{ HIDMAP_ABS_CB(HUP_GENERIC_DESKTOP, HUG_Y,	ps4dsmtp_data_cb) },
	{ HIDMAP_FINAL_CB(				ps4dsmtp_final_cb) },
};

static const struct hid_device_id ps4dshock_devs[] = {
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
	  HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x5c4),
	  HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0xba0),
	  HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD) },
};
static const struct hid_device_id ps4dsacc_devs[] = {
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
	  HID_TLC(HUP_GENERIC_DESKTOP, HUG_MULTIAXIS_CNTROLLER) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x5c4),
	  HID_TLC(HUP_GENERIC_DESKTOP, HUG_MULTIAXIS_CNTROLLER) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0xba0),
	  HID_TLC(HUP_GENERIC_DESKTOP, HUG_MULTIAXIS_CNTROLLER) },
};
static const struct hid_device_id ps4dshead_devs[] = {
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
	  HID_TLC(HUP_CONSUMER, HUC_HEADPHONE) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x5c4),
	  HID_TLC(HUP_CONSUMER, HUC_HEADPHONE) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0xba0),
	  HID_TLC(HUP_CONSUMER, HUC_HEADPHONE) },
};
static const struct hid_device_id ps4dsmtp_devs[] = {
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
	  HID_TLC(HUP_DIGITIZERS, HUD_TOUCHPAD) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x5c4),
	  HID_TLC(HUP_DIGITIZERS, HUD_TOUCHPAD) },
	{ HID_BVP(BUS_USB, USB_VENDOR_SONY, 0xba0),
	  HID_TLC(HUP_DIGITIZERS, HUD_TOUCHPAD) },
};

static int
ps4dshock_final_cb(HIDMAP_CB_ARGS)
{
	struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();

	if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) {
		evdev_support_prop(evdev, INPUT_PROP_DIRECT);
		evdev_set_cdev_mode(evdev, UID_ROOT, GID_GAMES,
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
	}

	/* Do not execute callback at interrupt handler and detach */
	return (ENOSYS);
}

static int
ps4dsacc_data_cb(HIDMAP_CB_ARGS)
{
	struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
	struct ps4dsacc_softc *sc = HIDMAP_CB_GET_SOFTC();
	struct ps4ds_calib_data *calib;
	u_int i;

	switch (HIDMAP_CB_GET_STATE()) {
	case HIDMAP_CB_IS_ATTACHING:
		for (i = 0; i < nitems(sc->calib_data); i++) {
			if (sc->calib_data[i].usage == ctx.hi->usage) {
				evdev_support_abs(evdev,
				     sc->calib_data[i].code,
				    -sc->calib_data[i].range,
				     sc->calib_data[i].range, 16, 0,
				     sc->calib_data[i].res);
				HIDMAP_CB_UDATA = &sc->calib_data[i];
				break;
			}
		}
		break;

	case HIDMAP_CB_IS_RUNNING:
		calib = HIDMAP_CB_UDATA;
		evdev_push_abs(evdev, calib->code,
		    ((int64_t)ctx.data - calib->bias) * calib->sens_numer /
		    calib->sens_denom);
		break;

	default:
		break;
	}

	return (0);
}

static int
ps4dsacc_tstamp_cb(HIDMAP_CB_ARGS)
{
	struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
	struct ps4dsacc_softc *sc = HIDMAP_CB_GET_SOFTC();
	uint16_t tstamp;

	switch (HIDMAP_CB_GET_STATE()) {
	case HIDMAP_CB_IS_ATTACHING:
		evdev_support_event(evdev, EV_MSC);
		evdev_support_msc(evdev, MSC_TIMESTAMP);
		break;

	case HIDMAP_CB_IS_RUNNING:
		/* Convert timestamp (in 5.33us unit) to timestamp_us */
		tstamp = (uint16_t)ctx.data;
		sc->ev_tstamp += (uint16_t)(tstamp - sc->hw_tstamp) * 16 / 3;
		sc->hw_tstamp = tstamp;
		evdev_push_msc(evdev, MSC_TIMESTAMP, sc->ev_tstamp);
		break;

	default:
		break;
	}

	return (0);
}

static int
ps4dsacc_final_cb(HIDMAP_CB_ARGS)
{
	struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();

	if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) {
		evdev_support_event(evdev, EV_ABS);
		evdev_support_prop(evdev, INPUT_PROP_ACCELEROMETER);
		evdev_set_cdev_mode(evdev, UID_ROOT, GID_GAMES,
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
	}
        /* Do not execute callback at interrupt handler and detach */
        return (ENOSYS);
}

static int
ps4dsmtp_npackets_cb(HIDMAP_CB_ARGS)
{
	struct ps4dsmtp_softc *sc = HIDMAP_CB_GET_SOFTC();

	if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_RUNNING) {
		sc->npackets = MIN(PS4DS_MAX_TOUCHPAD_PACKETS,(u_int)ctx.data);
		/* Reset pointer here as it is first usage in touchpad TLC */
		sc->data_ptr = sc->data;
	}

	return (0);
}

static int
ps4dsmtp_data_cb(HIDMAP_CB_ARGS)
{
	struct ps4dsmtp_softc *sc = HIDMAP_CB_GET_SOFTC();

	if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_RUNNING) {
		*sc->data_ptr = ctx.data;
		++sc->data_ptr;
	}

	return (0);
}

static void
ps4dsmtp_push_packet(struct ps4dsmtp_softc *sc, struct evdev_dev *evdev,
    int32_t *data)
{
	uint8_t hw_tstamp, delta;
	bool touch;

	evdev_push_abs(evdev, ABS_MT_SLOT, 0);
	if (data[PS4DS_TIP1] == 0) {
		evdev_push_abs(evdev, ABS_MT_TRACKING_ID, data[PS4DS_CID1]);
		evdev_push_abs(evdev, ABS_MT_POSITION_X, data[PS4DS_X1]);
		evdev_push_abs(evdev, ABS_MT_POSITION_Y, data[PS4DS_Y1]);
	} else
		evdev_push_abs(evdev, ABS_MT_TRACKING_ID, -1);
	evdev_push_abs(evdev, ABS_MT_SLOT, 1);
	if (data[PS4DS_TIP2] == 0) {
		evdev_push_abs(evdev, ABS_MT_TRACKING_ID, data[PS4DS_CID2]);
		evdev_push_abs(evdev, ABS_MT_POSITION_X, data[PS4DS_X2]);
		evdev_push_abs(evdev, ABS_MT_POSITION_Y, data[PS4DS_Y2]);
	} else
		evdev_push_abs(evdev, ABS_MT_TRACKING_ID, -1);

	if (sc->do_tstamps) {
		/*
		 * Export hardware timestamps in libinput-friendly way.
		 * Make timestamp counter 32-bit, scale up hardware
		 * timestamps to be on per 1usec basis and reset
		 * counter at the start of each touch.
		 */
		hw_tstamp = (uint8_t)data[PS4DS_TSTAMP];
		delta = hw_tstamp - sc->hw_tstamp;
		sc->hw_tstamp = hw_tstamp;
		touch = data[PS4DS_TIP1] == 0 || data[PS4DS_TIP2] == 0;
		/* Hardware timestamp counter ticks in 682 usec interval. */
		if ((touch || sc->touch) && delta != 0) {
			if (sc->touch)
				sc->ev_tstamp += delta * 682;
			evdev_push_msc(evdev, MSC_TIMESTAMP, sc->ev_tstamp);
		}
		if (!touch)
			sc->ev_tstamp = 0;
		sc->touch = touch;
	}
}

static int
ps4dsmtp_final_cb(HIDMAP_CB_ARGS)
{
	struct ps4dsmtp_softc *sc = HIDMAP_CB_GET_SOFTC();
	struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
	int32_t *data;

	switch (HIDMAP_CB_GET_STATE()) {
	case HIDMAP_CB_IS_ATTACHING:
		if (hid_test_quirk(hid_get_device_info(sc->hm.dev),
		    HQ_MT_TIMESTAMP))
			sc->do_tstamps = true;
		/*
		 * Dualshock 4 touchpad TLC contained in fixed report
		 * descriptor is almost compatible with MS precission touchpad
		 * specs and hmt(4) driver. But... for some reasons "Click"
		 * button location was grouped with other GamePad buttons by
		 * touchpad designers so it belongs to GamePad TLC. Fix it with
		 * direct reading of "Click" button value from interrupt frame.
		 */
		sc->btn_loc = (struct hid_location) { 1, 0, 49 };
		evdev_support_event(evdev, EV_SYN);
		evdev_support_event(evdev, EV_KEY);
		evdev_support_event(evdev, EV_ABS);
		if (sc->do_tstamps) {
			evdev_support_event(evdev, EV_MSC);
			evdev_support_msc(evdev, MSC_TIMESTAMP);
		}
		evdev_support_key(evdev, BTN_LEFT);
		evdev_support_abs(evdev, ABS_MT_SLOT, 0, 1, 0, 0, 0);
		evdev_support_abs(evdev, ABS_MT_TRACKING_ID, -1, 127, 0, 0, 0);
		evdev_support_abs(evdev, ABS_MT_POSITION_X, 0, 1920, 0, 0, 30);
		evdev_support_abs(evdev, ABS_MT_POSITION_Y, 0, 942, 0, 0, 49);
		evdev_support_prop(evdev, INPUT_PROP_POINTER);
		evdev_support_prop(evdev, INPUT_PROP_BUTTONPAD);
		evdev_set_flag(evdev, EVDEV_FLAG_MT_STCOMPAT);
		break;

	case HIDMAP_CB_IS_RUNNING:
		/* Only packets with ReportID=1 are accepted */
		if (HIDMAP_CB_GET_RID() != 1)
			return (ENOTSUP);
		evdev_push_key(evdev, BTN_LEFT,
		    HIDMAP_CB_GET_UDATA(&sc->btn_loc));
		for (data = sc->data;
		     data < sc->data + PS4DS_NTPUSAGES * sc->npackets;
		     data += PS4DS_NTPUSAGES) {
			ps4dsmtp_push_packet(sc, evdev, data);
			evdev_sync(evdev);
		}
		break;

	default:
		break;
	}

	/* Do execute callback at interrupt handler and detach */
	return (0);
}

static int
ps4dshock_write(struct ps4dshock_softc *sc)
{
	hid_size_t osize = sc->is_bluetooth ?
	    PS4DS_OUTPUT_REPORT11_SIZE : PS4DS_OUTPUT_REPORT5_SIZE;
	uint8_t buf[osize];
	int offset;
	bool led_on, led_blinks;

	memset(buf, 0, osize);
	buf[0] = sc->is_bluetooth ? 0x11 : 0x05;
	offset = sc->is_bluetooth ? 3 : 1;
	led_on = sc->led_state != PS4DS_LED_OFF;
	led_blinks = sc->led_state == PS4DS_LED_BLINKING;
	*(struct ps4ds_out5 *)(buf + offset) = (struct ps4ds_out5) {
		.features = 0x07, /* blink + LEDs + motor */
		.rumble_right = sc->rumble_right,
		.rumble_left = sc->rumble_left,
		.led_color_r = led_on ? sc->led_color.r : 0,
		.led_color_g = led_on ? sc->led_color.g : 0,
		.led_color_b = led_on ? sc->led_color.b : 0,
		/* convert milliseconds to centiseconds */
		.led_delay_on = led_blinks ? sc->led_delay_on / 10 : 0,
		.led_delay_off = led_blinks ? sc->led_delay_off / 10 : 0,
	};

	return (hid_write(sc->hm.dev, buf, osize));
}

/* Synaptics Touchpad */
static int
ps4dshock_sysctl(SYSCTL_HANDLER_ARGS)
{
	struct ps4dshock_softc *sc;
	int error, arg;

	if (oidp->oid_arg1 == NULL || oidp->oid_arg2 < 0 ||
	    oidp->oid_arg2 > PD4DSHOCK_SYSCTL_LAST)
		return (EINVAL);

	sc = oidp->oid_arg1;
	sx_xlock(&sc->lock);

	/* Read the current value. */
	arg = *(int *)((char *)sc + oidp->oid_arg2);
	error = sysctl_handle_int(oidp, &arg, 0, req);

	/* Sanity check. */
	if (error || !req->newptr)
		goto unlock;

	/*
	 * Check that the new value is in the concerned node's range
	 * of values.
	 */
	switch (oidp->oid_arg2) {
	case PD4DSHOCK_SYSCTL_LED_STATE:
		if (arg < 0 || arg >= PD4DS_LED_CNT)
			error = EINVAL;
		break;
	case PD4DSHOCK_SYSCTL_LED_COLOR_R:
	case PD4DSHOCK_SYSCTL_LED_COLOR_G:
	case PD4DSHOCK_SYSCTL_LED_COLOR_B:
		if (arg < 0 || arg > UINT8_MAX)
			error = EINVAL;
		break;
	case PD4DSHOCK_SYSCTL_LED_DELAY_ON:
	case PD4DSHOCK_SYSCTL_LED_DELAY_OFF:
		if (arg < 0 || arg > UINT8_MAX * 10)
			error = EINVAL;
		break;
	default:
		error = EINVAL;
	}

	/* Update. */
	if (error == 0) {
		*(int *)((char *)sc + oidp->oid_arg2) = arg;
		ps4dshock_write(sc);
	}
unlock:
	sx_unlock(&sc->lock);

	return (error);
}

static void
ps4dshock_identify(driver_t *driver, device_t parent)
{

	/* Overload PS4 DualShock gamepad rudimentary report descriptor */
	if (HIDBUS_LOOKUP_ID(parent, ps4dshock_devs) != NULL)
		hid_set_report_descr(parent, ps4dshock_rdesc,
		    sizeof(ps4dshock_rdesc));
}

static int
ps4dshock_probe(device_t dev)
{
	struct ps4dshock_softc *sc = device_get_softc(dev);

	hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR);
	return (
	    HIDMAP_PROBE(&sc->hm, dev, ps4dshock_devs, ps4dshock_map, NULL)
	);
}

static int
ps4dsacc_probe(device_t dev)
{
	struct ps4dsacc_softc *sc = device_get_softc(dev);

	hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR);
	return (
	    HIDMAP_PROBE(&sc->hm, dev, ps4dsacc_devs, ps4dsacc_map, "Sensors")
	);
}

static int
ps4dshead_probe(device_t dev)
{
	struct hidmap *hm = device_get_softc(dev);

	hidmap_set_debug_var(hm, &HID_DEBUG_VAR);
	return (
	    HIDMAP_PROBE(hm, dev, ps4dshead_devs, ps4dshead_map, "Headset")
	);
}

static int
ps4dsmtp_probe(device_t dev)
{
	struct ps4dshock_softc *sc = device_get_softc(dev);

	hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR);
	return (
	    HIDMAP_PROBE(&sc->hm, dev, ps4dsmtp_devs, ps4dsmtp_map, "Touchpad")
	);
}

static int
ps4dshock_attach(device_t dev)
{
	struct ps4dshock_softc *sc = device_get_softc(dev);
	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
	struct sysctl_oid *tree = device_get_sysctl_tree(dev);

	sc->led_state = PS4DS_LED_ON;
	sc->led_color = ps4ds_leds[device_get_unit(dev) % nitems(ps4ds_leds)];
	sc->led_delay_on = 500;	/* 1 Hz */
	sc->led_delay_off = 500;
	ps4dshock_write(sc);

	sx_init(&sc->lock, "ps4dshock");

	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
	    "led_state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
	    PD4DSHOCK_SYSCTL_LED_STATE, ps4dshock_sysctl, "I",
	    "LED state: 0 - off, 1 - on, 2 - blinking.");

	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
	    "led_color_r", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
	    PD4DSHOCK_SYSCTL_LED_COLOR_R, ps4dshock_sysctl, "I",
	    "LED color. Red component.");

	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
	    "led_color_g", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
	    PD4DSHOCK_SYSCTL_LED_COLOR_G, ps4dshock_sysctl, "I",
	    "LED color. Green component.");

	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
	    "led_color_b", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
	    PD4DSHOCK_SYSCTL_LED_COLOR_B, ps4dshock_sysctl, "I",
	    "LED color. Blue component.");

	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
	    "led_delay_on", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
	    PD4DSHOCK_SYSCTL_LED_DELAY_ON, ps4dshock_sysctl, "I",
	    "LED blink. On delay, msecs.");

	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
	    "led_delay_off", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
	    PD4DSHOCK_SYSCTL_LED_DELAY_OFF, ps4dshock_sysctl, "I",
	    "LED blink. Off delay, msecs.");

	return (hidmap_attach(&sc->hm));
}

static int
ps4dsacc_attach(device_t dev)
{
	struct ps4dsacc_softc *sc = device_get_softc(dev);
	uint8_t buf[PS4DS_FEATURE_REPORT2_SIZE];
	int error, speed_2x, range_2g;

	/* Read accelerometers and gyroscopes calibration data */
	error = hid_get_report(dev, buf, sizeof(buf), NULL,
	    HID_FEATURE_REPORT, 0x02);
	if (error)
		DPRINTF("get feature report failed, error=%d "
		    "(ignored)\n", error);

	DPRINTFN(5, "calibration data: %*D\n", (int)sizeof(buf), buf, " ");

	/*
	 * Set gyroscope calibration and normalization parameters.
	 * Data values will be normalized to 1/ PS4DS_GYRO_RES_PER_DEG_S
	 * degree/s.
	 */
#define HGETW(w) ((int16_t)((w)[0] | (((uint16_t)((w)[1])) << 8)))
	speed_2x = HGETW(&buf[19]) + HGETW(&buf[21]);
	sc->calib_data[0].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_RX);
	sc->calib_data[0].code = ABS_RX;
	sc->calib_data[0].range = PS4DS_GYRO_RES_PER_DEG_S * 2048;
	sc->calib_data[0].res = PS4DS_GYRO_RES_PER_DEG_S;
	sc->calib_data[0].bias = HGETW(&buf[1]);
	sc->calib_data[0].sens_numer = speed_2x * PS4DS_GYRO_RES_PER_DEG_S;
	sc->calib_data[0].sens_denom = HGETW(&buf[7]) - HGETW(&buf[9]);
	/* BT case */
	/* sc->calib_data[0].sens_denom = HGETW(&buf[7]) - HGETW(&buf[13]); */

	sc->calib_data[1].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_RY);
	sc->calib_data[1].code = ABS_RY;
	sc->calib_data[1].range = PS4DS_GYRO_RES_PER_DEG_S * 2048;
	sc->calib_data[1].res = PS4DS_GYRO_RES_PER_DEG_S;
	sc->calib_data[1].bias = HGETW(&buf[3]);
	sc->calib_data[1].sens_numer = speed_2x * PS4DS_GYRO_RES_PER_DEG_S;
	sc->calib_data[1].sens_denom = HGETW(&buf[11]) - HGETW(&buf[13]);
	/* BT case */
	/* sc->calib_data[1].sens_denom = HGETW(&buf[9]) - HGETW(&buf[15]); */

	sc->calib_data[2].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_RZ);
	sc->calib_data[2].code = ABS_RZ;
	sc->calib_data[2].range = PS4DS_GYRO_RES_PER_DEG_S * 2048;
	sc->calib_data[2].res = PS4DS_GYRO_RES_PER_DEG_S;
	sc->calib_data[2].bias = HGETW(&buf[5]);
	sc->calib_data[2].sens_numer = speed_2x * PS4DS_GYRO_RES_PER_DEG_S;
	sc->calib_data[2].sens_denom = HGETW(&buf[15]) - HGETW(&buf[17]);
	/* BT case */
	/* sc->calib_data[2].sens_denom = HGETW(&buf[11]) - HGETW(&buf[17]); */

	/*
	 * Set accelerometer calibration and normalization parameters.
	 * Data values will be normalized to 1 / PS4DS_ACC_RES_PER_G G.
	 */
	range_2g = HGETW(&buf[23]) - HGETW(&buf[25]);
	sc->calib_data[3].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X);
	sc->calib_data[3].code = ABS_X;
	sc->calib_data[3].range = PS4DS_ACC_RES_PER_G * 4;
	sc->calib_data[3].res = PS4DS_ACC_RES_PER_G;
	sc->calib_data[3].bias = HGETW(&buf[23]) - range_2g / 2;
	sc->calib_data[3].sens_numer = 2 * PS4DS_ACC_RES_PER_G;
	sc->calib_data[3].sens_denom = range_2g;

	range_2g = HGETW(&buf[27]) - HGETW(&buf[29]);
	sc->calib_data[4].usage =  HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y);
	sc->calib_data[4].code = ABS_Y;
	sc->calib_data[4].range = PS4DS_ACC_RES_PER_G * 4;
	sc->calib_data[4].res = PS4DS_ACC_RES_PER_G;
	sc->calib_data[4].bias = HGETW(&buf[27]) - range_2g / 2;
	sc->calib_data[4].sens_numer = 2 * PS4DS_ACC_RES_PER_G;
	sc->calib_data[4].sens_denom = range_2g;

	range_2g = HGETW(&buf[31]) - HGETW(&buf[33]);
	sc->calib_data[5].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z);
	sc->calib_data[5].code = ABS_Z;
	sc->calib_data[5].range = PS4DS_ACC_RES_PER_G * 4;
	sc->calib_data[5].res = PS4DS_ACC_RES_PER_G;
	sc->calib_data[5].bias = HGETW(&buf[31]) - range_2g / 2;
	sc->calib_data[5].sens_numer = 2 * PS4DS_ACC_RES_PER_G;
	sc->calib_data[5].sens_denom = range_2g;

	return (hidmap_attach(&sc->hm));
}

static int
ps4dshead_attach(device_t dev)
{
	return (hidmap_attach(device_get_softc(dev)));
}

static int
ps4dsmtp_attach(device_t dev)
{
	struct ps4dsmtp_softc *sc = device_get_softc(dev);

	return (hidmap_attach(&sc->hm));
}

static int
ps4dshock_detach(device_t dev)
{
	struct ps4dshock_softc *sc = device_get_softc(dev);

	hidmap_detach(&sc->hm);
	sc->led_state = PS4DS_LED_OFF;
	ps4dshock_write(sc);
	sx_destroy(&sc->lock);

	return (0);
}

static int
ps4dsacc_detach(device_t dev)
{
	struct ps4dsacc_softc *sc = device_get_softc(dev);

	return (hidmap_detach(&sc->hm));
}

static int
ps4dshead_detach(device_t dev)
{
	return (hidmap_detach(device_get_softc(dev)));
}

static int
ps4dsmtp_detach(device_t dev)
{
	struct ps4dsmtp_softc *sc = device_get_softc(dev);

	return (hidmap_detach(&sc->hm));
}

static device_method_t ps4dshock_methods[] = {
	DEVMETHOD(device_identify,	ps4dshock_identify),
	DEVMETHOD(device_probe,		ps4dshock_probe),
	DEVMETHOD(device_attach,	ps4dshock_attach),
	DEVMETHOD(device_detach,	ps4dshock_detach),

	DEVMETHOD_END
};
static device_method_t ps4dsacc_methods[] = {
	DEVMETHOD(device_probe,		ps4dsacc_probe),
	DEVMETHOD(device_attach,	ps4dsacc_attach),
	DEVMETHOD(device_detach,	ps4dsacc_detach),

	DEVMETHOD_END
};
static device_method_t ps4dshead_methods[] = {
	DEVMETHOD(device_probe,		ps4dshead_probe),
	DEVMETHOD(device_attach,	ps4dshead_attach),
	DEVMETHOD(device_detach,	ps4dshead_detach),

	DEVMETHOD_END
};
static device_method_t ps4dsmtp_methods[] = {
	DEVMETHOD(device_probe,		ps4dsmtp_probe),
	DEVMETHOD(device_attach,	ps4dsmtp_attach),
	DEVMETHOD(device_detach,	ps4dsmtp_detach),

	DEVMETHOD_END
};

DEFINE_CLASS_0(ps4dsacc, ps4dsacc_driver, ps4dsacc_methods,
    sizeof(struct ps4dsacc_softc));
DRIVER_MODULE(ps4dsacc, hidbus, ps4dsacc_driver, NULL, NULL);
DEFINE_CLASS_0(ps4dshead, ps4dshead_driver, ps4dshead_methods,
    sizeof(struct hidmap));
DRIVER_MODULE(ps4dshead, hidbus, ps4dshead_driver, NULL, NULL);
DEFINE_CLASS_0(ps4dsmtp, ps4dsmtp_driver, ps4dsmtp_methods,
    sizeof(struct ps4dsmtp_softc));
DRIVER_MODULE(ps4dsmtp, hidbus, ps4dsmtp_driver, NULL, NULL);
DEFINE_CLASS_0(ps4dshock, ps4dshock_driver, ps4dshock_methods,
    sizeof(struct ps4dshock_softc));
DRIVER_MODULE(ps4dshock, hidbus, ps4dshock_driver, NULL, NULL);

MODULE_DEPEND(ps4dshock, hid, 1, 1, 1);
MODULE_DEPEND(ps4dshock, hidbus, 1, 1, 1);
MODULE_DEPEND(ps4dshock, hidmap, 1, 1, 1);
MODULE_DEPEND(ps4dshock, hgame, 1, 1, 1);
MODULE_DEPEND(ps4dshock, evdev, 1, 1, 1);
MODULE_VERSION(ps4dshock, 1);
HID_PNP_INFO(ps4dshock_devs);
