USB Host Shield 2.0
PS5Parser.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2021 Kristian Sloth Lauszus. All rights reserved.
2 
3  This software may be distributed and modified under the terms of the GNU
4  General Public License version 2 (GPL2) as published by the Free Software
5  Foundation and appearing in the file GPL2.TXT included in the packaging of
6  this file. Please note that GPL2 Section 2[b] requires that all works based
7  on this software must also be made publicly available under the terms of
8  the GPL2 ("Copyleft").
9 
10  Contact information
11  -------------------
12 
13  Kristian Sloth Lauszus
14  Web : https://lauszus.com
15  e-mail : lauszus@gmail.com
16 
17  Thanks to Joseph Duchesne for the initial code.
18  Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port
19  and the series of patches found here: https://patchwork.kernel.org/project/linux-input/cover/20201219062336.72568-1-roderick@gaikai.com/
20  */
21 
22 #include "PS5Parser.h"
23 
24 enum DPADEnum {
25  DPAD_UP = 0x0,
27  DPAD_RIGHT = 0x2,
29  DPAD_DOWN = 0x4,
31  DPAD_LEFT = 0x6,
32  DPAD_LEFT_UP = 0x7,
33  DPAD_OFF = 0x8,
34 };
35 
36 // To enable serial debugging see "settings.h"
37 //#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller
38 
39 int8_t PS5Parser::getButtonIndexPS5(ButtonEnum b) {
40  const int8_t index = ButtonIndex(b);
41  if ((uint8_t) index >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0]))) return -1;
42  return index;
43 }
44 
45 bool PS5Parser::checkDpad(ButtonEnum b) {
46  switch (b) {
47  case UP:
48  return ps5Data.btn.dpad == DPAD_LEFT_UP || ps5Data.btn.dpad == DPAD_UP || ps5Data.btn.dpad == DPAD_UP_RIGHT;
49  case RIGHT:
50  return ps5Data.btn.dpad == DPAD_UP_RIGHT || ps5Data.btn.dpad == DPAD_RIGHT || ps5Data.btn.dpad == DPAD_RIGHT_DOWN;
51  case DOWN:
52  return ps5Data.btn.dpad == DPAD_RIGHT_DOWN || ps5Data.btn.dpad == DPAD_DOWN || ps5Data.btn.dpad == DPAD_DOWN_LEFT;
53  case LEFT:
54  return ps5Data.btn.dpad == DPAD_DOWN_LEFT || ps5Data.btn.dpad == DPAD_LEFT || ps5Data.btn.dpad == DPAD_LEFT_UP;
55  default:
56  return false;
57  }
58 }
59 
61  const int8_t index = getButtonIndexPS5(b); if (index < 0) return 0;
62  if (index <= LEFT) // Dpad
63  return checkDpad(b);
64  else
65  return ps5Data.btn.val & (1UL << pgm_read_byte(&PS5_BUTTONS[index]));
66 }
67 
69  const int8_t index = getButtonIndexPS5(b); if (index < 0) return 0;
70  uint32_t mask = 1UL << pgm_read_byte(&PS5_BUTTONS[index]);
71  bool click = buttonClickState.val & mask;
72  buttonClickState.val &= ~mask; // Clear "click" event
73  return click;
74 }
75 
77  const int8_t index = getButtonIndexPS5(b); if (index < 0) return 0;
78  if (index == ButtonIndex(L2)) // These are the only analog buttons on the controller
79  return ps5Data.trigger[0];
80  else if (index == ButtonIndex(R2))
81  return ps5Data.trigger[1];
82  return 0;
83 }
84 
86  return ps5Data.hatValue[(uint8_t)a];
87 }
88 
89 void PS5Parser::Parse(uint8_t len, uint8_t *buf) {
90  if (len > 1 && buf) {
91 #ifdef PRINTREPORT
92  Notify(PSTR("\r\nLen: "), 0x80); Notify(len, 0x80);
93  Notify(PSTR(", data: "), 0x80);
94  for (uint8_t i = 0; i < len; i++) {
95  D_PrintHex<uint8_t > (buf[i], 0x80);
96  Notify(PSTR(" "), 0x80);
97  }
98 #endif
99 
100  if (buf[0] == 0x01) // Check report ID
101  memcpy(&ps5Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps5Data)));
102  else if (buf[0] == 0x31) { // This report is send via Bluetooth, it has an offset of 1 compared to the USB data
103  if (len < 3) {
104 #ifdef DEBUG_USB_HOST
105  Notify(PSTR("\r\nReport is too short: "), 0x80);
106  D_PrintHex<uint8_t > (len, 0x80);
107 #endif
108  return;
109  }
110  memcpy(&ps5Data, buf + 2, min((uint8_t)(len - 2), MFK_CASTUINT8T sizeof(ps5Data)));
111  } else {
112 #ifdef DEBUG_USB_HOST
113  Notify(PSTR("\r\nUnknown report id: "), 0x80);
114  D_PrintHex<uint8_t > (buf[0], 0x80);
115  Notify(PSTR(", len: "), 0x80);
116  D_PrintHex<uint8_t > (len, 0x80);
117 #endif
118  return;
119  }
120 
121  if (ps5Data.btn.val != oldButtonState.val) { // Check if anything has changed
122  buttonClickState.val = ps5Data.btn.val & ~oldButtonState.val; // Update click state variable
123  oldButtonState.val = ps5Data.btn.val;
124 
125  // The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself
126  uint8_t newDpad = 0;
127  if (checkDpad(UP))
128  newDpad |= 1 << UP;
129  if (checkDpad(RIGHT))
130  newDpad |= 1 << RIGHT;
131  if (checkDpad(DOWN))
132  newDpad |= 1 << DOWN;
133  if (checkDpad(LEFT))
134  newDpad |= 1 << LEFT;
135  if (newDpad != oldDpad) {
136  buttonClickState.dpad = newDpad & ~oldDpad; // Override values
137  oldDpad = newDpad;
138  }
139  }
140 
141  message_counter++;
142  }
143 
145  sendOutputReport(&ps5Output); // Send output report
146 }
147 
148 
150  uint8_t i;
151  for (i = 0; i < sizeof(ps5Data.hatValue); i++)
152  ps5Data.hatValue[i] = 127; // Center value
153  ps5Data.btn.val = 0;
154  oldButtonState.val = 0;
155  for (i = 0; i < sizeof(ps5Data.trigger); i++)
156  ps5Data.trigger[i] = 0;
157  for (i = 0; i < sizeof(ps5Data.xy.finger)/sizeof(ps5Data.xy.finger[0]); i++)
158  ps5Data.xy.finger[i].touching = 1; // The bit is cleared if the finger is touching the touchpad
159 
160  ps5Data.btn.dpad = DPAD_OFF;
161  oldButtonState.dpad = DPAD_OFF;
162  buttonClickState.dpad = 0;
163  oldDpad = 0;
164 
165  leftTrigger.Reset();
167 
168  ps5Output.bigRumble = ps5Output.smallRumble = 0;
169  ps5Output.microphoneLed = 0;
170  ps5Output.disableLeds = 0;
171  ps5Output.playerLeds = 0;
172  ps5Output.r = ps5Output.g = ps5Output.b = 0;
173  ps5Output.reportChanged = false;
174 };
DPADEnum
Definition: PS4Parser.cpp:20
@ DPAD_DOWN_LEFT
Definition: PS5Parser.cpp:30
@ DPAD_RIGHT_DOWN
Definition: PS5Parser.cpp:28
@ DPAD_UP
Definition: PS5Parser.cpp:25
@ DPAD_DOWN
Definition: PS5Parser.cpp:29
@ DPAD_UP_RIGHT
Definition: PS5Parser.cpp:26
@ DPAD_LEFT_UP
Definition: PS5Parser.cpp:32
@ DPAD_OFF
Definition: PS5Parser.cpp:33
@ DPAD_RIGHT
Definition: PS5Parser.cpp:27
@ DPAD_LEFT
Definition: PS5Parser.cpp:31
const uint8_t PS5_BUTTONS[]
Definition: PS5Parser.h:30
void Reset()
Definition: PS5Parser.cpp:149
void Parse(uint8_t len, uint8_t *buf)
Definition: PS5Parser.cpp:89
PS5Trigger rightTrigger
Definition: PS5Parser.h:157
bool getButtonPress(ButtonEnum b)
Definition: PS5Parser.cpp:60
bool getButtonClick(ButtonEnum b)
Definition: PS5Parser.cpp:68
uint8_t getAnalogHat(AnalogHatEnum a)
Definition: PS5Parser.cpp:85
PS5Trigger leftTrigger
Definition: PS5Parser.h:154
virtual void sendOutputReport(PS5Output *output)=0
uint8_t getAnalogButton(ButtonEnum b)
Definition: PS5Parser.cpp:76
void Reset()
Definition: PS5Trigger.h:100
bool reportChanged
Definition: PS5Trigger.h:88
constexpr int8_t ButtonIndex(ButtonEnum key)
AnalogHatEnum
ButtonEnum
@ L2
@ R2
@ DOWN
@ UP
@ LEFT
@ RIGHT
#define Notify(...)
Definition: message.h:51
#define MFK_CASTUINT8T
Definition: settings.h:200
PS5Buttons btn
Definition: PS5Parser.h:112
uint8_t hatValue[4]
Definition: PS5Parser.h:107
uint8_t trigger[2]
Definition: PS5Parser.h:108
ps5TouchpadXY xy
Definition: PS5Parser.h:125
uint8_t smallRumble
Definition: PS5Parser.h:140
uint8_t b
Definition: PS5Parser.h:144
uint8_t microphoneLed
Definition: PS5Parser.h:141
uint8_t r
Definition: PS5Parser.h:144
bool reportChanged
Definition: PS5Parser.h:145
uint8_t playerLeds
Definition: PS5Parser.h:143
uint8_t disableLeds
Definition: PS5Parser.h:142
uint8_t bigRumble
Definition: PS5Parser.h:140
uint8_t g
Definition: PS5Parser.h:144
struct ps5TouchpadXY::@31 finger[2]
uint8_t touching
Definition: PS5Parser.h:84
uint8_t dpad
Definition: PS5Parser.h:58
uint32_t val
Definition: PS5Parser.h:78
#define pgm_read_byte(addr)
#define PSTR(str)