Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 1 | #include "motors/usb/hid.h" |
| 2 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 3 | namespace frc971::teensy { |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 4 | namespace { |
| 5 | |
| 6 | constexpr uint8_t hid_class() { return 0x03; } |
| 7 | |
| 8 | namespace hid_class_requests { |
| 9 | constexpr uint8_t get_report() { return 0x01; } |
| 10 | constexpr uint8_t get_idle() { return 0x02; } |
| 11 | constexpr uint8_t get_protocol() { return 0x03; } |
| 12 | constexpr uint8_t set_report() { return 0x09; } |
| 13 | constexpr uint8_t set_idle() { return 0x0a; } |
| 14 | constexpr uint8_t set_protcol() { return 0x0b; } |
| 15 | } // namespace hid_class_requests |
| 16 | |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 17 | } // namespace |
| 18 | |
| 19 | void HidFunction::Initialize() { |
| 20 | interface_ = AddInterface(); |
| 21 | in_endpoint_ = AddEndpoint(); |
| 22 | |
Brian Silverman | 4aa8304 | 2018-01-05 12:47:31 -0800 | [diff] [blame] | 23 | CreateIadDescriptor( |
| 24 | /*first_interface=*/interface_, |
| 25 | /*interface_count=*/1, |
| 26 | /*function_class=*/hid_class(), |
| 27 | /*function_subclass=*/0, |
| 28 | /*function_protocol=*/0, "HidIad"); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 29 | |
| 30 | { |
| 31 | const auto interface_descriptor = CreateDescriptor( |
| 32 | interface_descriptor_length(), UsbDescriptorType::kInterface); |
| 33 | interface_descriptor->AddByte(interface_); // bInterfaceNumber |
| 34 | interface_descriptor->AddByte(0); // bAlternateSetting |
| 35 | interface_descriptor->AddByte(1); // bNumEndpoints |
| 36 | interface_descriptor->AddByte(hid_class()); // bInterfaceClass |
| 37 | interface_descriptor->AddByte(0); // bInterfaceSubClass |
| 38 | interface_descriptor->AddByte(0); // bInterfaceProtocol |
| 39 | interface_descriptor->AddByte(device()->AddString("Hid")); // iInterface |
| 40 | } |
| 41 | |
Brian Silverman | 587edcc | 2018-01-15 12:00:22 -0800 | [diff] [blame] | 42 | { |
| 43 | const auto hid_descriptor = hid_descriptor_list_.CreateDescriptor( |
| 44 | 9, UsbClassDescriptorType::kHidHid); |
| 45 | hid_descriptor->AddUint16(0x0110); // bcdHID |
| 46 | hid_descriptor->AddByte(0); // bCountryCode |
| 47 | hid_descriptor->AddByte(1); // bNumDescriptors |
| 48 | hid_descriptor->AddByte(static_cast<uint8_t>( |
| 49 | UsbClassDescriptorType::kHidReport)); // bDescriptorType |
| 50 | hid_descriptor->AddUint16(report_descriptor_.size()); // wDescriptorLength |
| 51 | } |
| 52 | AddPremadeDescriptor(hid_descriptor_list_); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 53 | |
| 54 | { |
| 55 | const auto endpoint_descriptor = CreateDescriptor( |
| 56 | endpoint_descriptor_length(), UsbDescriptorType::kEndpoint); |
| 57 | endpoint_descriptor->AddByte(in_endpoint_ | |
| 58 | m_endpoint_address_in()); // bEndpointAddress |
| 59 | endpoint_descriptor->AddByte( |
| 60 | m_endpoint_attributes_interrupt()); // bmAttributes |
| 61 | endpoint_descriptor->AddUint16(in_endpoint_max_size()); // wMaxPacketSize |
Philipp Schrader | 790cb54 | 2023-07-05 21:06:52 -0700 | [diff] [blame] | 62 | endpoint_descriptor->AddByte(0x8); // bInterval |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 63 | } |
| 64 | } |
| 65 | |
| 66 | UsbFunction::SetupResponse HidFunction::HandleEndpoint0SetupPacket( |
| 67 | const UsbDevice::SetupPacket &setup_packet) { |
| 68 | if (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type) != |
| 69 | SetupRequestType::kClass) { |
| 70 | return SetupResponse::kIgnored; |
| 71 | } |
| 72 | if (G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type) != |
| 73 | standard_setup_recipients::kInterface) { |
| 74 | return SetupResponse::kIgnored; |
| 75 | } |
| 76 | if (setup_packet.index != interface_) { |
| 77 | return SetupResponse::kIgnored; |
| 78 | } |
| 79 | const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN; |
| 80 | switch (setup_packet.request) { |
| 81 | case hid_class_requests::get_report(): |
| 82 | if (!in) { |
| 83 | return SetupResponse::kStall; |
| 84 | } |
| 85 | // If it's not requesting the only Input report, no idea what the host |
| 86 | // wants so stall. |
| 87 | if (setup_packet.value != 0x0100) { |
| 88 | return SetupResponse::kStall; |
| 89 | } |
| 90 | { |
| 91 | DisableInterrupts disable_interrupts; |
| 92 | memcpy(get_report_response_buffer_.data(), |
| 93 | report_tx_buffer_being_sent(disable_interrupts), kMaxReportSize); |
| 94 | } |
| 95 | device()->QueueEndpoint0Data( |
| 96 | reinterpret_cast<const char *>(get_report_response_buffer_.data()), |
| 97 | ::std::min<uint16_t>(setup_packet.length, report_max_size_)); |
| 98 | return SetupResponse::kHandled; |
| 99 | |
| 100 | case hid_class_requests::set_idle(): |
| 101 | // Minimum implementation to make the host stack happy. |
| 102 | if (in) { |
| 103 | return SetupResponse::kStall; |
| 104 | } |
| 105 | device()->SendEmptyEndpoint0Packet(); |
| 106 | return SetupResponse::kHandled; |
| 107 | |
| 108 | // TODO(Brian): Should we actually implement the idle stuff? |
| 109 | |
| 110 | default: |
| 111 | return SetupResponse::kStall; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | UsbFunction::SetupResponse HidFunction::HandleGetDescriptor( |
| 116 | const UsbDevice::SetupPacket &setup_packet) { |
| 117 | const uint8_t recipient = |
| 118 | G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type); |
| 119 | if (recipient != standard_setup_recipients::kInterface) { |
| 120 | return SetupResponse::kIgnored; |
| 121 | } |
| 122 | |
| 123 | const uint8_t descriptor_type = (setup_packet.value >> 8) & 0xFF; |
| 124 | if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type) != |
| 125 | standard_descriptor_type_types::kClass) { |
| 126 | return SetupResponse::kIgnored; |
| 127 | } |
| 128 | if (setup_packet.index != interface_) { |
| 129 | return SetupResponse::kIgnored; |
| 130 | } |
| 131 | |
| 132 | const uint8_t descriptor_index = setup_packet.value & 0xFF; |
| 133 | switch (descriptor_type) { |
| 134 | case static_cast<uint8_t>(UsbClassDescriptorType::kHidHid): |
| 135 | if (descriptor_index != 0) { |
| 136 | return SetupResponse::kStall; |
| 137 | } |
| 138 | device()->QueueEndpoint0Data( |
Brian Silverman | 587edcc | 2018-01-15 12:00:22 -0800 | [diff] [blame] | 139 | hid_descriptor_list_.GetData(), |
| 140 | ::std::min<int>(setup_packet.length, |
| 141 | hid_descriptor_list_.CurrentSize())); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 142 | return SetupResponse::kHandled; |
| 143 | |
| 144 | case static_cast<uint8_t>(UsbClassDescriptorType::kHidReport): |
| 145 | if (descriptor_index != 0) { |
| 146 | return SetupResponse::kStall; |
| 147 | } |
| 148 | device()->QueueEndpoint0Data( |
Brian Silverman | 587edcc | 2018-01-15 12:00:22 -0800 | [diff] [blame] | 149 | report_descriptor_.data(), |
| 150 | ::std::min<int>(setup_packet.length, report_descriptor_.size())); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 151 | return SetupResponse::kHandled; |
| 152 | |
| 153 | case static_cast<uint8_t>(UsbClassDescriptorType::kHidPhysical): |
| 154 | static constexpr char kNoPhysicalDescriptors[] = {0, 0, 0}; |
| 155 | device()->QueueEndpoint0Data( |
| 156 | kNoPhysicalDescriptors, |
| 157 | ::std::min<int>(setup_packet.length, sizeof(kNoPhysicalDescriptors))); |
| 158 | return SetupResponse::kHandled; |
| 159 | } |
| 160 | return SetupResponse::kStall; |
| 161 | } |
| 162 | |
| 163 | void HidFunction::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/, |
| 164 | EvenOdd odd) { |
| 165 | if (endpoint == in_endpoint_) { |
| 166 | DisableInterrupts disable_interrupts; |
| 167 | if (odd != BufferStateToEmpty(tx_state_)) { |
| 168 | __builtin_trap(); |
| 169 | } |
| 170 | |
| 171 | // Copy the current one into the just-sent buffer. |
| 172 | memcpy(report_tx_buffer_being_sent(disable_interrupts), |
| 173 | report_tx_buffer_to_fill(disable_interrupts), kMaxReportSize); |
| 174 | |
| 175 | dma_memory_barrier(); |
| 176 | device()->SetBdtEntry( |
| 177 | in_endpoint_, Direction::kTx, BufferStateToFill(tx_state_), |
| 178 | {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) | |
| 179 | static_cast<uint32_t>(next_tx_toggle_), |
| 180 | report_tx_buffer_to_fill(disable_interrupts)}); |
| 181 | |
| 182 | // Advance the state to indicate we've swapped buffers. |
| 183 | tx_state_ = BufferStateAfterFill(BufferStateAfterEmpty(tx_state_)); |
| 184 | next_tx_toggle_ = Data01Inverse(next_tx_toggle_); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | void HidFunction::HandleConfigured(int endpoint) { |
| 189 | if (endpoint == in_endpoint_) { |
| 190 | device()->ConfigureEndpointFor(in_endpoint_, false, true, true); |
| 191 | DisableInterrupts disable_interrupts; |
| 192 | next_tx_toggle_ = Data01::kData0; |
| 193 | |
| 194 | EvenOdd to_fill; |
| 195 | if (BufferStateHasFull(tx_state_)) { |
| 196 | to_fill = BufferStateToEmpty(tx_state_); |
| 197 | } else { |
| 198 | to_fill = BufferStateToFill(tx_state_); |
| 199 | tx_state_ = BufferStateAfterFill(tx_state_); |
| 200 | } |
| 201 | |
| 202 | dma_memory_barrier(); |
| 203 | device()->SetBdtEntry( |
| 204 | in_endpoint_, Direction::kTx, to_fill, |
| 205 | {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) | |
| 206 | static_cast<uint32_t>(next_tx_toggle_), |
| 207 | report_tx_buffer_to_fill(disable_interrupts)}); |
| 208 | next_tx_toggle_ = Data01Inverse(next_tx_toggle_); |
| 209 | } |
| 210 | } |
| 211 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 212 | } // namespace frc971::teensy |