Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement TinyUSB HID driver back end #1412

Merged
merged 12 commits into from
Mar 22, 2024
4 changes: 4 additions & 0 deletions src/kaleidoscope/driver/hid/HIDDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@

#pragma once

#ifndef USE_TINYUSB
#include "tusb_hid.h"
#else
#include "class/hid/hid.h"
#endif
51 changes: 51 additions & 0 deletions src/kaleidoscope/driver/hid/TinyUSB.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// -*- mode: c++ -*-
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2024 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "kaleidoscope/driver/hid/Base.h" // for Base, BaseProps
#include "kaleidoscope/driver/hid/tinyusb/AbsoluteMouse.h" // for AbsoluteMouse, AbsoluteMou...
#include "kaleidoscope/driver/hid/tinyusb/Keyboard.h"
#include "kaleidoscope/driver/hid/tinyusb/MultiReport.h"

#ifdef USE_TINYUSB

namespace kaleidoscope {
namespace driver {
namespace hid {

struct TinyUSBProps : public BaseProps {
typedef tinyusb::KeyboardProps KeyboardProps;
typedef tinyusb::Keyboard<KeyboardProps> Keyboard;
typedef tinyusb::MouseProps MouseProps;
typedef tinyusb::Mouse<MouseProps> Mouse;
#if CFG_TUD_HID > 2
typedef tinyusb::AbsoluteMouseProps AbsoluteMouseProps;
typedef tinyusb::AbsoluteMouse<AbsoluteMouseProps> AbsoluteMouse;
#else
#warning "omitting AbsoluteMouse because CFG_TUD_HID is too small"
#endif
};

template<typename _Props>
class TinyUSB : public Base<_Props> {};

} // namespace hid
} // namespace driver
} // namespace kaleidoscope

#endif /* USE_TINY_USB */
44 changes: 44 additions & 0 deletions src/kaleidoscope/driver/hid/tinyusb/AbsoluteMouse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// -*- mode: c++ -*-
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2024 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifdef USE_TINYUSB

#include "AbsoluteMouse.h"

namespace kaleidoscope {
namespace driver {
namespace hid {
namespace tinyusb {

static const uint8_t AbsoluteMouseDesc[] = {
DESCRIPTOR_ABSOLUTE_MOUSE(),
};

TUSBAbsoluteMouse_::TUSBAbsoluteMouse_()
: HIDD(AbsoluteMouseDesc, sizeof(AbsoluteMouseDesc), HID_ITF_PROTOCOL_NONE, 1) {}

TUSBAbsoluteMouse_ &TUSBAbsoluteMouse() {
static TUSBAbsoluteMouse_ obj;
return obj;
}

} // namespace tinyusb
} // namespace hid
} // namespace driver
} // namespace kaleidoscope

#endif /* USE_TINYUSB */
86 changes: 86 additions & 0 deletions src/kaleidoscope/driver/hid/tinyusb/AbsoluteMouse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// -*- mode: c++ -*-
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2024 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#ifdef USE_TINYUSB

#include <stdint.h> // for uint8_t, int8_t, uint16_t

#include "Adafruit_TinyUSB.h"
#include "HIDD.h"
#include "kaleidoscope/driver/hid/apis/AbsoluteMouseAPI.h" // for AbsoluteMouse, AbsoluteMouseProps

// IWYU pragma: no_include "DeviceAPIs/AbsoluteMouseAPI.hpp"

namespace kaleidoscope {
namespace driver {
namespace hid {
namespace tinyusb {

class TUSBAbsoluteMouse_ : public AbsoluteMouseAPI, public HIDD {
public:
TUSBAbsoluteMouse_();
void begin() {
(void)HIDD::begin();
AbsoluteMouseAPI::begin();
}
void sendReport(void *data, int length) override {
(void)HIDD::sendReport(0, data, length);
}
};

extern TUSBAbsoluteMouse_ &TUSBAbsoluteMouse();

class AbsoluteMouseWrapper {
public:
AbsoluteMouseWrapper() {}

void begin() {
TUSBAbsoluteMouse().begin();
}
void move(int8_t x, int8_t y, int8_t wheel) {
TUSBAbsoluteMouse().move(x, y, wheel);
}
void moveTo(uint16_t x, uint16_t y, uint8_t wheel) {
TUSBAbsoluteMouse().moveTo(x, y, wheel);
}

void click(uint8_t buttons) {
TUSBAbsoluteMouse().click(buttons);
}
void press(uint8_t buttons) {
TUSBAbsoluteMouse().press(buttons);
}
void release(uint8_t buttons) {
TUSBAbsoluteMouse().release(buttons);
}
};

struct AbsoluteMouseProps : public base::AbsoluteMouseProps {
typedef AbsoluteMouseWrapper AbsoluteMouse;
};

template<typename _Props>
class AbsoluteMouse : public base::AbsoluteMouse<_Props> {};

} // namespace tinyusb
} // namespace hid
} // namespace driver
} // namespace kaleidoscope

#endif /* USE_TINYUSB */
91 changes: 91 additions & 0 deletions src/kaleidoscope/driver/hid/tinyusb/HIDD.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// -*- mode: c++ -*-
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2024 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#ifdef USE_TINYUSB

#include <stdint.h> // for uint8_t, int8_t, uint16_t

#include "Adafruit_TinyUSB.h"
#include "kaleidoscope/driver/hid/Base.h"
#include "kaleidoscope/driver/hid/base/Keyboard.h"
#include "kaleidoscope/driver/hid/apis/ConsumerControlAPI.h"
#include "kaleidoscope/driver/hid/apis/MouseAPI.h"
#include "kaleidoscope/driver/hid/apis/SystemControlAPI.h"

// IWYU pragma: no_include "DeviceAPIs/AbsoluteMouseAPI.hpp"

namespace kaleidoscope {
namespace driver {
namespace hid {
namespace tinyusb {

class HIDD : public Adafruit_USBD_HID {
public:
HIDD(uint8_t const *desc_report,
uint16_t len,
uint8_t protocol = HID_ITF_PROTOCOL_NONE,
uint8_t interval_ms = 4)
: Adafruit_USBD_HID(desc_report, len, protocol, interval_ms) {}

protected:
bool sendReport(uint8_t report_id, void const *report, uint8_t len) {
if (TinyUSBDevice.suspended()) {
TinyUSBDevice.remoteWakeup();
/*
* Wait for 30ms, which is the minimum required by spec for a resume.
* (USB 2.0 §7.1.7.7)
*
* 20ms of host echoing the resume downstream, and 10ms of recovery time
* once the bus has resumed.
*/
delay(30);
}
/*
* Block for up to 250ms for endpoint to become ready. If the host is polling,
* this should take about 1ms (the requested polling interval, but the
* host is allowed to poll less frequently). On resume, especially remote
* wakeup on macOS, not only can the resume take longer much than the
* minimum (over 2 seconds, instead of 20ms), the recovery period can be
* over 100ms (instead of 10ms).
*
* This will mitigate, but not necessarily eliminate, stuck key situations
* during resume. One scenario is a key down report being queued while in
* the recovery period, where the bus has resumed, but the host isn't
* polling yet. The endpoint will be ready, but the transmission won't
* happen until the host resumes polling. The transmission of the key up
* event might time out, leading to a stuck key situation.
*
* Unfortunately, we usually can't abort a packet transmission once queued.
*/
for (int timeout = 250; timeout-- && !ready();) {
delay(1);
}
if (!ready()) {
return false;
}
return Adafruit_USBD_HID::sendReport(report_id, report, len);
}
};

} // namespace tinyusb
} // namespace hid
} // namespace driver
} // namespace kaleidoscope

#endif /* USE_TINYUSB */
80 changes: 80 additions & 0 deletions src/kaleidoscope/driver/hid/tinyusb/Keyboard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// -*- mode: c++ -*-
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2024 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifdef USE_TINYUSB

#include "kaleidoscope/driver/hid/tinyusb/Keyboard.h"

namespace kaleidoscope {
namespace driver {
namespace hid {
namespace tinyusb {

static uint8_t BootKeyboardDesc[] = {
DESCRIPTOR_BOOT_KEYBOARD(),
};

static uint8_t HybridKeyboardDesc[] = {
DESCRIPTOR_HYBRID_KEYBOARD(),
};

uint8_t BootKeyboard_::leds = 0;

void boot_keyboard_set_report_cb(
uint8_t report_id,
hid_report_type_t report_type,
uint8_t const *buffer,
uint16_t bufsize) {
if (report_id != 0) {
return;
}
if (report_type != HID_REPORT_TYPE_OUTPUT) {
return;
}
if (bufsize != 1) {
return;
}
BootKeyboard_::leds = buffer[0];
}

BootKeyboard_::BootKeyboard_(uint8_t bootkb_only_)
: BootKeyboardAPI(bootkb_only_),
HIDD(bootkb_only_ ? BootKeyboardDesc : HybridKeyboardDesc,
bootkb_only_ ? sizeof(BootKeyboardDesc) : sizeof(HybridKeyboardDesc),
HID_ITF_PROTOCOL_KEYBOARD,
1) {
setReportCallback(NULL, boot_keyboard_set_report_cb);
}

void BootKeyboard_::setReportDescriptor(uint8_t bootkb_only) {
if (bootkb_only) {
Adafruit_USBD_HID::setReportDescriptor(BootKeyboardDesc, sizeof(BootKeyboardDesc));
} else {
Adafruit_USBD_HID::setReportDescriptor(HybridKeyboardDesc, sizeof(HybridKeyboardDesc));
}
}

BootKeyboard_ &BootKeyboard() {
static BootKeyboard_ obj;
return obj;
}

} // namespace tinyusb
} // namespace hid
} // namespace driver
} // namespace kaleidoscope
#endif
Loading
Loading