#include <sys/cdefs.h>
#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,
0x09, 0x05,
0xA1, 0x01,
0x85, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x33,
0x09, 0x34,
0x15, 0x00,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x04,
0x81, 0x02,
0x09, 0x39,
0x15, 0x00,
0x25, 0x07,
0x35, 0x00,
0x46, 0x3B, 0x01,
0x65, 0x14,
0x75, 0x04,
0x95, 0x01,
0x81, 0x42,
0x65, 0x00,
0x45, 0x00,
0x05, 0x09,
0x19, 0x01,
0x29, 0x0E,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x0E,
0x81, 0x02,
0x06, 0x00, 0xFF,
0x09, 0x20,
0x75, 0x06,
0x95, 0x01,
0x15, 0x00,
0x25, 0x3F,
0x81, 0x02,
0x05, 0x01,
0x09, 0x32,
0x09, 0x35,
0x15, 0x00,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x02,
0x81, 0x02,
0xC0,
0x05, 0x01,
0x09, 0x08,
0xA1, 0x01,
0x06, 0x00, 0xFF,
0x09, 0x21,
0x27, 0xFF, 0xFF, 0x00, 0x00,
0x75, 0x10,
0x95, 0x01,
0x81, 0x02,
0x05, 0x06,
0x09, 0x20,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x19, 0x33,
0x29, 0x35,
0x16, 0x00, 0x80,
0x26, 0xFF, 0x7F,
0x75, 0x10,
0x95, 0x03,
0x81, 0x02,
0x19, 0x30,
0x29, 0x32,
0x16, 0x00, 0x80,
0x26, 0xFF, 0x7F,
0x95, 0x03,
0x81, 0x02,
0x06, 0x00, 0xFF,
0x09, 0x21,
0x15, 0x00,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x05,
0x81, 0x03,
0xC0,
0x05, 0x0C,
0x09, 0x05,
0xA1, 0x01,
0x75, 0x05,
0x95, 0x01,
0x81, 0x03,
0x06, 0x00, 0xFF,
0x09, 0x20,
0x09, 0x21,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x02,
0x81, 0x02,
0x75, 0x01,
0x95, 0x01,
0x81, 0x03,
0x75, 0x08,
0x95, 0x02,
0x81, 0x03,
0xC0,
0x05, 0x0D,
0x09, 0x05,
0xA1, 0x01,
0x06, 0x00, 0xFF,
0x09, 0x21,
0x15, 0x00,
0x25, 0x03,
0x75, 0x04,
0x95, 0x01,
0x81, 0x02,
0x75, 0x04,
0x95, 0x01,
0x81, 0x03,
0x05, 0x0D,
0x09, 0x56,
0x55, 0x0C,
0x66, 0x01, 0x10,
0x46, 0xCC, 0x06,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x01,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0x05, 0x0D,
0x09, 0x22,
0xA1, 0x02,
0x09, 0x51,
0x25, 0x7F,
0x75, 0x07,
0x95, 0x01,
0x81, 0x02,
0x09, 0x42,
0x25, 0x01,
0x75, 0x01,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x09, 0x30,
0x55, 0x0E,
0x65, 0x11,
0x35, 0x00,
0x46, 0xB8, 0x01,
0x26, 0x80, 0x07,
0x75, 0x0C,
0x81, 0x02,
0x09, 0x31,
0x46, 0xC0, 0x00,
0x26, 0xAE, 0x03,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0xC0,
0x05, 0x0D,
0x09, 0x22,
0xA1, 0x02,
0x09, 0x51,
0x25, 0x7F,
0x75, 0x07,
0x95, 0x01,
0x81, 0x02,
0x09, 0x42,
0x25, 0x01,
0x75, 0x01,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x09, 0x30,
0x55, 0x0E,
0x65, 0x11,
0x35, 0x00,
0x46, 0xB8, 0x01,
0x26, 0x80, 0x07,
0x75, 0x0C,
0x81, 0x02,
0x09, 0x31,
0x46, 0xC0, 0x00,
0x26, 0xAE, 0x03,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0xC0,
0x05, 0x0D,
0x09, 0x56,
0x55, 0x0C,
0x66, 0x01, 0x10,
0x46, 0xCC, 0x06,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x01,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0x05, 0x0D,
0x09, 0x22,
0xA1, 0x02,
0x09, 0x51,
0x25, 0x7F,
0x75, 0x07,
0x95, 0x01,
0x81, 0x02,
0x09, 0x42,
0x25, 0x01,
0x75, 0x01,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x09, 0x30,
0x55, 0x0E,
0x65, 0x11,
0x35, 0x00,
0x46, 0xB8, 0x01,
0x26, 0x80, 0x07,
0x75, 0x0C,
0x81, 0x02,
0x09, 0x31,
0x46, 0xC0, 0x00,
0x26, 0xAE, 0x03,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0xC0,
0x05, 0x0D,
0x09, 0x22,
0xA1, 0x02,
0x09, 0x51,
0x25, 0x7F,
0x75, 0x07,
0x95, 0x01,
0x81, 0x02,
0x09, 0x42,
0x25, 0x01,
0x75, 0x01,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x09, 0x30,
0x55, 0x0E,
0x65, 0x11,
0x35, 0x00,
0x46, 0xB8, 0x01,
0x26, 0x80, 0x07,
0x75, 0x0C,
0x81, 0x02,
0x09, 0x31,
0x46, 0xC0, 0x00,
0x26, 0xAE, 0x03,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0xC0,
0x05, 0x0D,
0x09, 0x56,
0x55, 0x0C,
0x66, 0x01, 0x10,
0x46, 0xCC, 0x06,
0x26, 0xFF, 0x00,
0x75, 0x08,
0x95, 0x01,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0x05, 0x0D,
0x09, 0x22,
0xA1, 0x02,
0x09, 0x51,
0x25, 0x7F,
0x75, 0x07,
0x95, 0x01,
0x81, 0x02,
0x09, 0x42,
0x25, 0x01,
0x75, 0x01,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x09, 0x30,
0x55, 0x0E,
0x65, 0x11,
0x35, 0x00,
0x46, 0xB8, 0x01,
0x26, 0x80, 0x07,
0x75, 0x0C,
0x81, 0x02,
0x09, 0x31,
0x46, 0xC0, 0x00,
0x26, 0xAE, 0x03,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0xC0,
0x05, 0x0D,
0x09, 0x22,
0xA1, 0x02,
0x09, 0x51,
0x25, 0x7F,
0x75, 0x07,
0x95, 0x01,
0x81, 0x02,
0x09, 0x42,
0x25, 0x01,
0x75, 0x01,
0x95, 0x01,
0x81, 0x02,
0x05, 0x01,
0x09, 0x30,
0x55, 0x0E,
0x65, 0x11,
0x35, 0x00,
0x46, 0xB8, 0x01,
0x26, 0x80, 0x07,
0x75, 0x0C,
0x81, 0x02,
0x09, 0x31,
0x46, 0xC0, 0x00,
0x26, 0xAE, 0x03,
0x81, 0x02,
0x65, 0x00,
0x45, 0x00,
0xC0,
0x75, 0x08,
0x95, 0x03,
0x81, 0x03,
0x85, 0x05,
0x06, 0x00, 0xFF,
0x09, 0x22,
0x15, 0x00,
0x26, 0xFF, 0x00,
0x95, 0x1F,
0x91, 0x02,
0x85, 0x04,
0x09, 0x23,
0x95, 0x24,
0xB1, 0x02,
0x85, 0x02,
0x09, 0x24,
0x95, 0x24,
0xB1, 0x02,
0x85, 0x08,
0x09, 0x25,
0x95, 0x03,
0xB1, 0x02,
0x85, 0x10,
0x09, 0x26,
0x95, 0x04,
0xB1, 0x02,
0x85, 0x11,
0x09, 0x27,
0x95, 0x02,
0xB1, 0x02,
0x85, 0x12,
0x06, 0x02, 0xFF,
0x09, 0x21,
0x95, 0x0F,
0xB1, 0x02,
0x85, 0x13,
0x09, 0x22,
0x95, 0x16,
0xB1, 0x02,
0x85, 0x14,
0x06, 0x05, 0xFF,
0x09, 0x20,
0x95, 0x10,
0xB1, 0x02,
0x85, 0x15,
0x09, 0x21,
0x95, 0x2C,
0xB1, 0x02,
0x06, 0x80, 0xFF,
0x85, 0x80,
0x09, 0x20,
0x95, 0x06,
0xB1, 0x02,
0x85, 0x81,
0x09, 0x21,
0x95, 0x06,
0xB1, 0x02,
0x85, 0x82,
0x09, 0x22,
0x95, 0x05,
0xB1, 0x02,
0x85, 0x83,
0x09, 0x23,
0x95, 0x01,
0xB1, 0x02,
0x85, 0x84,
0x09, 0x24,
0x95, 0x04,
0xB1, 0x02,
0x85, 0x85,
0x09, 0x25,
0x95, 0x06,
0xB1, 0x02,
0x85, 0x86,
0x09, 0x26,
0x95, 0x06,
0xB1, 0x02,
0x85, 0x87,
0x09, 0x27,
0x95, 0x23,
0xB1, 0x02,
0x85, 0x88,
0x09, 0x28,
0x95, 0x22,
0xB1, 0x02,
0x85, 0x89,
0x09, 0x29,
0x95, 0x02,
0xB1, 0x02,
0x85, 0x90,
0x09, 0x30,
0x95, 0x05,
0xB1, 0x02,
0x85, 0x91,
0x09, 0x31,
0x95, 0x03,
0xB1, 0x02,
0x85, 0x92,
0x09, 0x32,
0x95, 0x03,
0xB1, 0x02,
0x85, 0x93,
0x09, 0x33,
0x95, 0x0C,
0xB1, 0x02,
0x85, 0xA0,
0x09, 0x40,
0x95, 0x06,
0xB1, 0x02,
0x85, 0xA1,
0x09, 0x41,
0x95, 0x01,
0xB1, 0x02,
0x85, 0xA2,
0x09, 0x42,
0x95, 0x01,
0xB1, 0x02,
0x85, 0xA3,
0x09, 0x43,
0x95, 0x30,
0xB1, 0x02,
0x85, 0xA4,
0x09, 0x44,
0x95, 0x0D,
0xB1, 0x02,
0x85, 0xA5,
0x09, 0x45,
0x95, 0x15,
0xB1, 0x02,
0x85, 0xA6,
0x09, 0x46,
0x95, 0x15,
0xB1, 0x02,
0x85, 0xF0,
0x09, 0x47,
0x95, 0x3F,
0xB1, 0x02,
0x85, 0xF1,
0x09, 0x48,
0x95, 0x3F,
0xB1, 0x02,
0x85, 0xF2,
0x09, 0x49,
0x95, 0x0F,
0xB1, 0x02,
0x85, 0xA7,
0x09, 0x4A,
0x95, 0x01,
0xB1, 0x02,
0x85, 0xA8,
0x09, 0x4B,
0x95, 0x01,
0xB1, 0x02,
0x85, 0xA9,
0x09, 0x4C,
0x95, 0x08,
0xB1, 0x02,
0x85, 0xAA,
0x09, 0x4E,
0x95, 0x01,
0xB1, 0x02,
0x85, 0xAB,
0x09, 0x4F,
0x95, 0x39,
0xB1, 0x02,
0x85, 0xAC,
0x09, 0x50,
0x95, 0x39,
0xB1, 0x02,
0x85, 0xAD,
0x09, 0x51,
0x95, 0x0B,
0xB1, 0x02,
0x85, 0xAE,
0x09, 0x52,
0x95, 0x01,
0xB1, 0x02,
0x85, 0xAF,
0x09, 0x53,
0x95, 0x02,
0xB1, 0x02,
0x85, 0xB0,
0x09, 0x54,
0x95, 0x3F,
0xB1, 0x02,
0xC0,
};
#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;
uint8_t led_delay_off;
} __attribute__((packed));
static const struct ps4ds_led {
int r;
int g;
int b;
} ps4ds_leds[] = {
{ 0x00, 0x00, 0x40 },
{ 0x40, 0x00, 0x00 },
{ 0x00, 0x40, 0x00 },
{ 0x20, 0x00, 0x20 },
{ 0x02, 0x01, 0x00 },
{ 0x00, 0x01, 0x01 },
{ 0x01, 0x01, 0x01 }
};
enum ps4ds_led_state {
PS4DS_LED_OFF,
PS4DS_LED_ON,
PS4DS_LED_BLINKING,
PD4DS_LED_CNT,
};
struct ps4ds_calib_data {
int32_t usage;
int32_t code;
int32_t res;
int32_t range;
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;
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),
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);
}
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:
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);
}
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);
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) {
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;
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;
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:
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;
}
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,
.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,
.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));
}
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);
arg = *(int *)((char *)sc + oidp->oid_arg2);
error = sysctl_handle_int(oidp, &arg, 0, req);
if (error || !req->newptr)
goto unlock;
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;
}
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)
{
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;
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;
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, " ");
#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]);
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]);
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]);
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);