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