Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame^] | 1 | #ifndef MOTORS_USB_HID_H_ |
| 2 | #define MOTORS_USB_HID_H_ |
| 3 | |
| 4 | #include <stdint.h> |
| 5 | #include <string.h> |
| 6 | #include <array> |
| 7 | |
| 8 | #include "motors/usb/usb.h" |
| 9 | #include "motors/util.h" |
| 10 | |
| 11 | namespace frc971 { |
| 12 | namespace teensy { |
| 13 | |
| 14 | // Implements an HID class device. |
| 15 | // TODO(Brian): Make this way more generic. |
| 16 | class HidFunction final : public UsbFunction { |
| 17 | public: |
| 18 | HidFunction(UsbDevice *device, int report_max_size) |
| 19 | : UsbFunction(device), report_max_size_(report_max_size) { |
| 20 | if (report_max_size_ > kMaxReportSize) { |
| 21 | __builtin_trap(); |
| 22 | } |
| 23 | } |
| 24 | ~HidFunction() override = default; |
| 25 | |
| 26 | void UpdateReport(const void *data, int length, |
| 27 | const DisableInterrupts &disable_interrupts) { |
| 28 | memcpy(report_tx_buffer_to_fill(disable_interrupts), data, length); |
| 29 | } |
| 30 | |
| 31 | private: |
| 32 | // Choose 64 for now so it always fits into a single packet. |
| 33 | static constexpr int kMaxReportSize = 64; |
| 34 | |
| 35 | uint8_t *report_tx_buffer_for(EvenOdd odd) { |
| 36 | return report_tx_buffers_[EvenOddIndex(odd)].data(); |
| 37 | } |
| 38 | uint8_t *report_tx_buffer_being_sent(const DisableInterrupts &) { |
| 39 | return report_tx_buffer_for(BufferStateToEmpty(tx_state_)); |
| 40 | } |
| 41 | uint8_t *report_tx_buffer_to_fill(const DisableInterrupts &) { |
| 42 | return report_tx_buffer_for(BufferStateToFill(tx_state_)); |
| 43 | } |
| 44 | |
| 45 | int in_endpoint_max_size() const { return report_max_size_; } |
| 46 | |
| 47 | void Initialize() override; |
| 48 | |
| 49 | SetupResponse HandleEndpoint0SetupPacket( |
| 50 | const UsbDevice::SetupPacket &setup_packet) override; |
| 51 | SetupResponse HandleGetDescriptor( |
| 52 | const UsbDevice::SetupPacket &setup_packet) override; |
| 53 | |
| 54 | void HandleOutFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/) override { |
| 55 | // We don't support any out endpoints. |
| 56 | } |
| 57 | |
| 58 | void HandleInFinished(int endpoint, BdtEntry *bdt_entry, |
| 59 | EvenOdd odd) override; |
| 60 | |
| 61 | void HandleConfigured(int endpoint) override; |
| 62 | void HandleReset() override { |
| 63 | tx_state_ = EndpointBufferState::kBothEmptyEvenFirst; |
| 64 | device()->SetBdtEntry(in_endpoint_, Direction::kTx, EvenOdd::kEven, |
| 65 | {0, nullptr}); |
| 66 | device()->SetBdtEntry(in_endpoint_, Direction::kTx, EvenOdd::kOdd, |
| 67 | {0, nullptr}); |
| 68 | { |
| 69 | DisableInterrupts disable_interrupts; |
| 70 | memset(report_tx_buffers_[0].data(), 0, kMaxReportSize); |
| 71 | memset(report_tx_buffers_[1].data(), 0, kMaxReportSize); |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | ::std::array<::std::array<uint8_t, kMaxReportSize>, 2> report_tx_buffers_; |
| 76 | ::std::array<uint8_t, kMaxReportSize> get_report_response_buffer_; |
| 77 | |
| 78 | // This is only manipulated with interrupts disabled. |
| 79 | EndpointBufferState tx_state_; |
| 80 | Data01 next_tx_toggle_; |
| 81 | |
| 82 | // Our interface number. |
| 83 | int interface_; |
| 84 | |
| 85 | // The IN endpoint we send reports on. |
| 86 | int in_endpoint_; |
| 87 | |
| 88 | const int report_max_size_; |
| 89 | }; |
| 90 | |
| 91 | } // namespace teensy |
| 92 | } // namespace frc971 |
| 93 | |
| 94 | #endif // MOTORS_USB_HID_H_ |