Brian Silverman | eda63f3 | 2017-10-08 18:57:33 -0400 | [diff] [blame] | 1 | #ifndef MOTORS_USB_CDC_H_ |
| 2 | #define MOTORS_USB_CDC_H_ |
| 3 | |
| 4 | #include <array> |
| 5 | |
| 6 | #include "motors/usb/usb.h" |
| 7 | #include "motors/usb/queue.h" |
| 8 | #include "motors/util.h" |
| 9 | |
| 10 | // CDC (Communications Device Class) is a "class standard" which takes 30 pages |
| 11 | // to explain that a communications device has communications and data, and they |
| 12 | // can come via separate interfaces or the same ones, and the data can be in a |
| 13 | // lot of formats. The only commonality across the "class" seems to be some |
| 14 | // constants and a few descriptors. |
| 15 | |
| 16 | namespace frc971 { |
| 17 | namespace teensy { |
| 18 | |
| 19 | struct CdcLineCoding { |
| 20 | static constexpr uint8_t stop_bits_1() { return 0; } |
| 21 | static constexpr uint8_t stop_bits_1_5() { return 1; } |
| 22 | static constexpr uint8_t stop_bits_2() { return 2; } |
| 23 | |
| 24 | static constexpr uint8_t parity_none() { return 0; } |
| 25 | static constexpr uint8_t parity_odd() { return 1; } |
| 26 | static constexpr uint8_t parity_even() { return 2; } |
| 27 | static constexpr uint8_t parity_mark() { return 3; } |
| 28 | static constexpr uint8_t parity_space() { return 4; } |
| 29 | |
| 30 | // The baud rate in bits/second. |
| 31 | uint32_t rate; // dwDTERate |
| 32 | |
| 33 | uint8_t stop_bits; // bCharFormat |
| 34 | |
| 35 | uint8_t parity; // bParityType |
| 36 | |
| 37 | // 5, 6, 7, 8, or 16 according to the standard. |
| 38 | uint8_t data_bits; // bDataBits |
| 39 | } __attribute__((packed)); |
| 40 | static_assert(sizeof(CdcLineCoding) == 7, "wrong size"); |
| 41 | |
| 42 | // Implements a pretty dumb serial port via CDC's ACM (Abstract Control |
| 43 | // Management) and Call Management functions. |
| 44 | class AcmTty final : public UsbFunction { |
| 45 | public: |
| 46 | AcmTty(UsbDevice *device) : UsbFunction(device) {} |
| 47 | ~AcmTty() override = default; |
| 48 | |
| 49 | size_t Read(void *buffer, size_t buffer_size); |
| 50 | size_t Write(const void *buffer, size_t buffer_size); |
| 51 | |
Brian Silverman | 19ea60f | 2018-01-03 21:43:15 -0800 | [diff] [blame] | 52 | bool write_queue_empty() const { return tx_queue_.empty(); } |
| 53 | |
Brian Silverman | eda63f3 | 2017-10-08 18:57:33 -0400 | [diff] [blame] | 54 | private: |
| 55 | enum class NextEndpoint0Out { |
| 56 | kNone, |
| 57 | kLineCoding, |
| 58 | }; |
| 59 | |
| 60 | // We're going with the largest allowable sizes for full speed devices for |
| 61 | // the data endpoints to maximize throughput. |
| 62 | static constexpr uint16_t kDataMaxPacketSize = 64; |
| 63 | |
| 64 | // We have no information to send here, so no point allocating bus bandwidth |
| 65 | // for it. |
| 66 | static constexpr uint16_t kStatusMaxPacketSize = 1; |
| 67 | |
Brian Silverman | eda63f3 | 2017-10-08 18:57:33 -0400 | [diff] [blame] | 68 | void Initialize() override; |
| 69 | |
| 70 | SetupResponse HandleEndpoint0SetupPacket( |
| 71 | const UsbDevice::SetupPacket &setup_packet) override; |
| 72 | SetupResponse HandleEndpoint0OutPacket(void *data, int data_length) override; |
| 73 | void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) override; |
| 74 | void HandleInFinished(int endpoint, BdtEntry *bdt_entry, |
| 75 | EvenOdd odd) override; |
| 76 | void HandleConfigured(int endpoint) override; |
| 77 | void HandleReset() override { |
| 78 | // TODO(Brian): Handle data already in the buffers correctly. |
| 79 | DisableInterrupts disable_interrupts; |
| 80 | tx_state_ = EndpointBufferState::kBothEmptyEvenFirst; |
| 81 | device()->SetBdtEntry(status_endpoint_, Direction::kTx, EvenOdd::kEven, |
| 82 | {0, nullptr}); |
| 83 | device()->SetBdtEntry(status_endpoint_, Direction::kTx, EvenOdd::kOdd, |
| 84 | {0, nullptr}); |
| 85 | device()->SetBdtEntry(data_tx_endpoint_, Direction::kTx, EvenOdd::kEven, |
| 86 | {0, nullptr}); |
| 87 | device()->SetBdtEntry(data_tx_endpoint_, Direction::kTx, EvenOdd::kOdd, |
| 88 | {0, nullptr}); |
| 89 | device()->SetBdtEntry(data_rx_endpoint_, Direction::kRx, EvenOdd::kEven, |
| 90 | {0, nullptr}); |
| 91 | device()->SetBdtEntry(data_rx_endpoint_, Direction::kRx, EvenOdd::kOdd, |
| 92 | {0, nullptr}); |
| 93 | } |
| 94 | |
| 95 | char *tx_buffer_for(EvenOdd odd) { |
| 96 | return tx_buffers_[EvenOddIndex(odd)].data(); |
| 97 | } |
| 98 | |
| 99 | void EnqueueTxData(const DisableInterrupts &); |
| 100 | |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 101 | ::std::array<::std::array<char, kDataMaxPacketSize>, 2> tx_buffers_, |
| 102 | rx_buffers_; |
| 103 | |
Brian Silverman | eda63f3 | 2017-10-08 18:57:33 -0400 | [diff] [blame] | 104 | // In theory, we could sent notifications over this about things like the line |
| 105 | // state, but we don't have anything to report so we pretty much just ignore |
| 106 | // this. |
| 107 | int status_interface_; |
| 108 | int data_interface_; |
| 109 | int status_endpoint_, data_tx_endpoint_, data_rx_endpoint_; |
| 110 | |
| 111 | Queue tx_queue_{1024}, rx_queue_{1024}; |
| 112 | |
| 113 | NextEndpoint0Out next_endpoint0_out_ = NextEndpoint0Out::kNone; |
| 114 | |
| 115 | // This is only manipulated with interrupts disabled. |
| 116 | EndpointBufferState tx_state_; |
| 117 | Data01 next_tx_toggle_; |
| 118 | |
Brian Silverman | 4aa8304 | 2018-01-05 12:47:31 -0800 | [diff] [blame] | 119 | // These are BdtEntries which we're holding onto without releasing back to the |
Brian Silverman | eda63f3 | 2017-10-08 18:57:33 -0400 | [diff] [blame] | 120 | // hardware so that we won't receive any more data until we have space for it. |
| 121 | // They are only manipulated with interrupts disabled. |
| 122 | BdtEntry *first_rx_held_ = nullptr, *second_rx_held_ = nullptr; |
| 123 | Data01 next_rx_toggle_; |
| 124 | |
| 125 | CdcLineCoding line_coding_{0, CdcLineCoding::stop_bits_1(), |
| 126 | CdcLineCoding::parity_none(), 8}; |
| 127 | CdcLineCoding line_coding_to_send_; |
| 128 | |
| 129 | uint16_t control_line_state_ = 0; |
| 130 | }; |
| 131 | |
| 132 | } // namespace teensy |
| 133 | } // namespace frc971 |
| 134 | |
| 135 | #endif // MOTORS_USB_CDC_H_ |