Add a simple HID device
It currently has a hard-coded report of 4 2-byte joystick values
plus 8 buttons.
Also fix a few things I noticed in the CDC code while working on the HID
code.
Change-Id: Icce6c6ad686fdf974924daab6cb40b7b7d0f7996
diff --git a/motors/usb/hid.h b/motors/usb/hid.h
new file mode 100644
index 0000000..4b2dae4
--- /dev/null
+++ b/motors/usb/hid.h
@@ -0,0 +1,94 @@
+#ifndef MOTORS_USB_HID_H_
+#define MOTORS_USB_HID_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <array>
+
+#include "motors/usb/usb.h"
+#include "motors/util.h"
+
+namespace frc971 {
+namespace teensy {
+
+// Implements an HID class device.
+// TODO(Brian): Make this way more generic.
+class HidFunction final : public UsbFunction {
+ public:
+ HidFunction(UsbDevice *device, int report_max_size)
+ : UsbFunction(device), report_max_size_(report_max_size) {
+ if (report_max_size_ > kMaxReportSize) {
+ __builtin_trap();
+ }
+ }
+ ~HidFunction() override = default;
+
+ void UpdateReport(const void *data, int length,
+ const DisableInterrupts &disable_interrupts) {
+ memcpy(report_tx_buffer_to_fill(disable_interrupts), data, length);
+ }
+
+ private:
+ // Choose 64 for now so it always fits into a single packet.
+ static constexpr int kMaxReportSize = 64;
+
+ uint8_t *report_tx_buffer_for(EvenOdd odd) {
+ return report_tx_buffers_[EvenOddIndex(odd)].data();
+ }
+ uint8_t *report_tx_buffer_being_sent(const DisableInterrupts &) {
+ return report_tx_buffer_for(BufferStateToEmpty(tx_state_));
+ }
+ uint8_t *report_tx_buffer_to_fill(const DisableInterrupts &) {
+ return report_tx_buffer_for(BufferStateToFill(tx_state_));
+ }
+
+ int in_endpoint_max_size() const { return report_max_size_; }
+
+ void Initialize() override;
+
+ SetupResponse HandleEndpoint0SetupPacket(
+ const UsbDevice::SetupPacket &setup_packet) override;
+ SetupResponse HandleGetDescriptor(
+ const UsbDevice::SetupPacket &setup_packet) override;
+
+ void HandleOutFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/) override {
+ // We don't support any out endpoints.
+ }
+
+ void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
+ EvenOdd odd) override;
+
+ void HandleConfigured(int endpoint) override;
+ void HandleReset() override {
+ tx_state_ = EndpointBufferState::kBothEmptyEvenFirst;
+ device()->SetBdtEntry(in_endpoint_, Direction::kTx, EvenOdd::kEven,
+ {0, nullptr});
+ device()->SetBdtEntry(in_endpoint_, Direction::kTx, EvenOdd::kOdd,
+ {0, nullptr});
+ {
+ DisableInterrupts disable_interrupts;
+ memset(report_tx_buffers_[0].data(), 0, kMaxReportSize);
+ memset(report_tx_buffers_[1].data(), 0, kMaxReportSize);
+ }
+ }
+
+ ::std::array<::std::array<uint8_t, kMaxReportSize>, 2> report_tx_buffers_;
+ ::std::array<uint8_t, kMaxReportSize> get_report_response_buffer_;
+
+ // This is only manipulated with interrupts disabled.
+ EndpointBufferState tx_state_;
+ Data01 next_tx_toggle_;
+
+ // Our interface number.
+ int interface_;
+
+ // The IN endpoint we send reports on.
+ int in_endpoint_;
+
+ const int report_max_size_;
+};
+
+} // namespace teensy
+} // namespace frc971
+
+#endif // MOTORS_USB_HID_H_