-
Notifications
You must be signed in to change notification settings - Fork 786
/
SwitchProUSB.h
156 lines (132 loc) · 6.34 KB
/
SwitchProUSB.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* Copyright (C) 2021 Kristian Sloth Lauszus. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Kristian Sloth Lauszus
Web : https://lauszus.com
e-mail : [email protected]
*/
#ifndef _switch_pro_usb_h_
#define _switch_pro_usb_h_
#include "hiduniversal.h"
#include "SwitchProParser.h"
#define SWITCH_PRO_VID 0x057E // Nintendo Corporation
#define SWITCH_PRO_PID 0x2009 // Switch Pro Controller
/**
* This class implements support for the Switch Pro controller via USB.
* It uses the HIDUniversal class for all the USB communication.
*/
class SwitchProUSB : public HIDUniversal, public SwitchProParser {
public:
/**
* Constructor for the SwitchProUSB class.
* @param p Pointer to the USB class instance.
*/
SwitchProUSB(USB *p) :
HIDUniversal(p) {
SwitchProParser::Reset();
};
/**
* Used to check if a Switch Pro controller is connected.
* @return Returns true if it is connected.
*/
bool connected() {
return HIDUniversal::isReady() && HIDUniversal::VID == SWITCH_PRO_VID && HIDUniversal::PID == SWITCH_PRO_PID;
};
/**
* Used to call your own function when the device is successfully initialized.
* @param funcOnInit Function to call.
*/
void attachOnInit(void (*funcOnInit)(void)) {
pFuncOnInit = funcOnInit;
};
protected:
/** @name HIDUniversal implementation */
/**
* Used to parse USB HID data.
* @param hid Pointer to the HID class.
* @param is_rpt_id Only used for Hubs.
* @param len The length of the incoming data.
* @param buf Pointer to the data buffer.
*/
virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) {
if (HIDUniversal::VID == SWITCH_PRO_VID && HIDUniversal::PID == SWITCH_PRO_PID)
SwitchProParser::Parse(len, buf);
};
/**
* Called when a device is successfully initialized.
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
* This is useful for instance if you want to set the LEDs in a specific way.
*/
virtual uint8_t OnInitSuccessful() {
if (HIDUniversal::VID == SWITCH_PRO_VID && HIDUniversal::PID == SWITCH_PRO_PID) {
SwitchProParser::Reset();
// We need to send a handshake and disable the timeout or the Pro controller will stop sending data via USB
// We can not send the commands quickly after each other, so we simply send out the commands at the same
// rate as the controller is sending data
switchProOutput.sendHandshake = switchProOutput.disableTimeout = true;
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
else {
setLedOn(LED1); // Turn on the LED1
setLedHomeOn(); // Turn on the home LED
}
}
return 0;
};
/**@}*/
/** @name SwitchProParser implementation */
virtual void sendOutputReport(uint8_t *data, uint8_t len) {
// Based on: https://github.com/Dan611/hid-procon
// The first 8 bytes are always the same. The actual report follows
uint8_t buf[8 + len];
buf[0] = 0x80; // PROCON_REPORT_SEND_USB
buf[1] = 0x92; // PROCON_USB_DO_CMD
buf[2] = 0x00;
buf[3] = 0x31;
buf[4] = 0x00;
buf[5] = 0x00;
buf[6] = 0x00;
buf[7] = 0x00;
// Cope over the report
memcpy(buf + 8, data, len);
// Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data
SetReport(epInfo[0].epAddr, 0, 0x02, buf[0], sizeof(buf), buf);
};
virtual void sendHandshake() {
switchProOutput.sendHandshake = false;
// See: https://github.com/Dan611/hid-procon/blob/master/hid-procon.c
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/USB-HID-Notes.md
uint8_t buf[2] = { 0x80 /* PROCON_REPORT_SEND_USB */, 0x02 /* PROCON_USB_HANDSHAKE */ };
// Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data
SetReport(epInfo[0].epAddr, 0, 0x02, buf[0], sizeof(buf), buf);
};
virtual void disableTimeout() {
switchProOutput.disableTimeout = false;
// See: https://github.com/Dan611/hid-procon/blob/master/hid-procon.c
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/USB-HID-Notes.md
uint8_t buf[2] = { 0x80 /* PROCON_REPORT_SEND_USB */, 0x04 /* PROCON_USB_ENABLE */ };
// Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data
SetReport(epInfo[0].epAddr, 0, 0x02, buf[0], sizeof(buf), buf);
};
/**@}*/
/** @name USBDeviceConfig implementation */
/**
* Used by the USB core to check what this driver support.
* @param vid The device's VID.
* @param pid The device's PID.
* @return Returns true if the device's VID and PID matches this driver.
*/
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
return (vid == SWITCH_PRO_VID && pid == SWITCH_PRO_PID);
};
/**@}*/
private:
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
};
#endif