-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Gravis GamePad Pro support (GrIP)
- Loading branch information
Filipp Andjelo
committed
Jun 1, 2021
1 parent
aa0dfea
commit bf6336f
Showing
4 changed files
with
225 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// This file is part of Necroware's GamePort adapter firmware. | ||
// Copyright (C) 2021 Necroware | ||
// | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include "DigitalPin.h" | ||
|
||
/// Class to communicate with Gravis joysticks using GrIP. | ||
/// @remark This is a green field implementation, but it was heavily | ||
/// inspired by Linux Sidewinder driver implementation. See | ||
/// https://github.com/torvalds/linux/blob/master/drivers/input/joystick/grip.c | ||
class GrIP { | ||
public: | ||
|
||
/// Supported Gravis model types. | ||
/// | ||
/// @remark currently inly GamePad Pro is supported | ||
enum class Model { | ||
|
||
/// Unknown model | ||
GRIP_UNKNOWN, | ||
|
||
/// GamePad Pro | ||
GRIP_GAMEPAD_PRO, | ||
}; | ||
|
||
/// Joystick state. | ||
struct State { | ||
uint8_t axis[2]{0}; | ||
uint16_t buttons{0}; | ||
}; | ||
|
||
/// Resets the joystick and tries to detect the model. | ||
void reset() { | ||
m_model = Model::GRIP_UNKNOWN; | ||
while (m_model == Model::GRIP_UNKNOWN) { | ||
if (readPacket()) { | ||
m_model = Model::GRIP_GAMEPAD_PRO; | ||
} | ||
} | ||
} | ||
|
||
/// Gets the detected model. | ||
/// @returns the detected joystick model | ||
Model getModel() const { | ||
return m_model; | ||
} | ||
|
||
/// Reads the joystick state. | ||
/// @returns the state of axis, buttons etc. | ||
/// @remark if reading the state fails, the last known state is | ||
/// returned and the joystick reset is executed. | ||
State readState() { | ||
const auto packet = readPacket(); | ||
if (packet) { | ||
|
||
const auto getBit = [&](uint8_t pos) { | ||
return uint8_t(packet >> pos) & 1; | ||
}; | ||
|
||
m_state.axis[0] = 1 + getBit(13) - getBit(12); | ||
m_state.axis[1] = 1 + getBit(15) - getBit(16); | ||
|
||
m_state.buttons = getBit(8); | ||
m_state.buttons |= getBit(3) << 1; | ||
m_state.buttons |= getBit(7) << 2; | ||
m_state.buttons |= getBit(6) << 3; | ||
m_state.buttons |= getBit(10) << 4; | ||
m_state.buttons |= getBit(11) << 5; | ||
m_state.buttons |= getBit(5) << 6; | ||
m_state.buttons |= getBit(2) << 7; | ||
m_state.buttons |= getBit(0) << 8; | ||
m_state.buttons |= getBit(1) << 9; | ||
} | ||
|
||
return m_state; | ||
} | ||
|
||
private: | ||
|
||
DigitalInput<GamePort<2>::pin, true> m_clock; | ||
DigitalInput<GamePort<7>::pin, true> m_data; | ||
Model m_model{Model::GRIP_UNKNOWN}; | ||
State m_state; | ||
|
||
/// Read bits packet from the joystick. | ||
uint32_t readPacket() const { | ||
|
||
// Gravis GamePad Pro sends 24 bits long packages of data all the | ||
// time. Every package starts with a binary tag sequence 011111. | ||
|
||
static const auto length = 24u; | ||
uint32_t result = 0u; | ||
|
||
// read a package of 24 bits | ||
for (auto i = 0u; i < length; i++) { | ||
if (!m_clock.wait(Edge::falling, 100)) { | ||
return 0u; | ||
} | ||
result |= uint32_t(m_data.get()) << i; | ||
} | ||
|
||
// alighn the bits to have the binary tag in front. This code | ||
// was taken almost unchanged from the linux kernel. | ||
for (auto i = 0u; i < length; i++) { | ||
result = (result >> 1) | (result & 1) << (length - 1u); | ||
if ((result & 0xfe4210) == 0x7c0000) { | ||
return result; | ||
} | ||
} | ||
return 0u; | ||
} | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// This file is part of Necroware's GamePort adapter firmware. | ||
// Copyright (C) 2021 Necroware | ||
// | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
#pragma once | ||
|
||
#include "DigitalPin.h" | ||
#include "Driver.h" | ||
#include "GamePort.h" | ||
#include "HidDevice.h" | ||
#include "GrIP.h" | ||
#include "Log.h" | ||
|
||
class HidGrIP : public Driver { | ||
public: | ||
|
||
template <GrIP::Model M> struct HidType; | ||
using HidGamePadPro = HidDevice<HidType<GrIP::Model::GRIP_GAMEPAD_PRO>>; | ||
|
||
void init() override { | ||
m_grip.reset(); | ||
switch(m_grip.getModel()) { | ||
case GrIP::Model::GRIP_GAMEPAD_PRO: | ||
log("Detected Gravis GamePad Pro"); | ||
HidGamePadPro::activate(); | ||
break; | ||
case GrIP::Model::GRIP_UNKNOWN: | ||
log("Unknown or not GrIP input device"); | ||
break; | ||
} | ||
} | ||
|
||
void update() override { | ||
const auto state = m_grip.readState(); | ||
switch(m_grip.getModel()) { | ||
case GrIP::Model::GRIP_GAMEPAD_PRO: | ||
sendGamePadPro(state); | ||
break; | ||
case GrIP::Model::GRIP_UNKNOWN: | ||
log("Unknown or not GrIP input device"); | ||
break; | ||
} | ||
} | ||
|
||
private: | ||
|
||
static void sendGamePadPro(const GrIP::State& state) { | ||
const uint16_t data = state.buttons << 4 | state.axis[0] << 2 | state.axis[1]; | ||
HidGamePadPro::send(&data, sizeof(data)); | ||
} | ||
|
||
GrIP m_grip; | ||
}; | ||
|
||
template <> | ||
const byte HidGrIP::HidGamePadPro::description[] = { | ||
0x05, 0x01, // Usage Page (Generic Desktop) | ||
0x09, 0x04, // Usage (Joystick) | ||
0xa1, 0x01, // Collection (Application) | ||
0x85, id, // Report ID (id) | ||
0x05, 0x01, // Usage Page (Generic Desktop) | ||
0x09, 0x30, // Usage (X) | ||
0x09, 0x31, // Usage (Y) | ||
0x15, 0x00, // Logical Minimum (0) | ||
0x25, 0x02, // Logical Maximum (2) | ||
0x75, 0x02, // Report Size (2) | ||
0x95, 0x02, // Report Count (2) | ||
0x81, 0x02, // Input (Data,Var,Abs) | ||
0x05, 0x09, // Usage Page (Button) | ||
0x19, 0x01, // Usage Minimum (1) | ||
0x29, 0x0A, // Usage Maximum (10) | ||
0x15, 0x00, // Logical Minimum (0) | ||
0x25, 0x01, // Logical Maximum (1) | ||
0x75, 0x01, // Report Size (1) | ||
0x95, 0x0A, // Report Count (10) | ||
0x81, 0x02, // Input (Data,Var,Abs) | ||
0x75, 0x02, // Report Size (2) | ||
0x95, 0x01, // Report Count (1) | ||
0x81, 0x03, // Input (Const,Var,Abs) | ||
0xc0, // End Collection | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters