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/pistol_grip/BUILD b/motors/pistol_grip/BUILD
index 364801b..b1239c5 100644
--- a/motors/pistol_grip/BUILD
+++ b/motors/pistol_grip/BUILD
@@ -8,9 +8,10 @@
],
deps = [
'//motors:util',
+ '//motors/core',
'//motors/usb',
'//motors/usb:cdc',
- '//motors/core',
+ '//motors/usb:hid',
],
restricted_to = mcu_cpus,
)
diff --git a/motors/pistol_grip/drivers_station.cc b/motors/pistol_grip/drivers_station.cc
index 8632a8d..26f171e 100644
--- a/motors/pistol_grip/drivers_station.cc
+++ b/motors/pistol_grip/drivers_station.cc
@@ -2,17 +2,21 @@
// communicates over CAN with the one in the pistol grip controller.
#include <stdio.h>
+#include <atomic>
#include "motors/core/time.h"
#include "motors/core/kinetis.h"
#include "motors/usb/usb.h"
#include "motors/usb/cdc.h"
+#include "motors/usb/hid.h"
#include "motors/util.h"
namespace frc971 {
namespace motors {
namespace {
+::std::atomic<teensy::AcmTty *> global_stdout{nullptr};
+
void EchoChunks(teensy::AcmTty *tty1) {
while (true) {
char buffer[512];
@@ -93,16 +97,41 @@
}
}
+void MoveJoysticks(teensy::HidFunction *joysticks) {
+ static uint8_t x_axis = 0, y_axis = 97, z_axis = 5, rz_axis = 8;
+ while (true) {
+ {
+ DisableInterrupts disable_interrupts;
+ uint8_t buttons = 0;
+ if (x_axis % 8u) {
+ buttons = 2;
+ }
+ uint8_t report[10] = {x_axis, 0, y_axis, 0, z_axis,
+ rz_axis, 0, 0, buttons, 0};
+ joysticks->UpdateReport(report, 10, disable_interrupts);
+ }
+ delay(1);
+ x_axis += 1;
+ y_axis += 3;
+ z_axis += 5;
+ rz_axis += 8;
+ }
+}
+
} // namespace
extern "C" {
+
void *__stack_chk_guard = (void *)0x67111971;
-int _write(int file, char *ptr, int len) {
- (void)file;
- (void)ptr;
- (void)len;
- return -1;
+
+int _write(int /*file*/, char *ptr, int len) {
+ teensy::AcmTty *const tty = global_stdout.load(::std::memory_order_acquire);
+ if (tty != nullptr) {
+ return tty->Write(ptr, len);
+ }
+ return 0;
}
+
} // extern "C"
void __stack_chk_fail(void);
@@ -127,13 +156,20 @@
delay(100);
- teensy::UsbDevice usb_device(0, 0x16c0, 0x0490);
+ teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
usb_device.SetManufacturer("FRC 971 Spartan Robotics");
usb_device.SetProduct("Pistol Grip Controller interface");
+ teensy::HidFunction joysticks(&usb_device, 10);
+ // TODO(Brian): Figure out why Windows refuses to recognize the joystick along
+ // with a TTY or two.
+#if 0
teensy::AcmTty tty1(&usb_device);
+ teensy::AcmTty tty2(&usb_device);
+ global_stdout.store(&tty2, ::std::memory_order_release);
+#endif
usb_device.Initialize();
- WriteData(&tty1);
+ MoveJoysticks(&joysticks);
return 0;
}
diff --git a/motors/usb/BUILD b/motors/usb/BUILD
index 950de0a..7d43447 100644
--- a/motors/usb/BUILD
+++ b/motors/usb/BUILD
@@ -102,3 +102,19 @@
'//aos/testing:googletest',
],
)
+
+cc_library(
+ name = 'hid',
+ visibility = ['//visibility:public'],
+ hdrs = [
+ 'hid.h',
+ ],
+ srcs = [
+ 'hid.cc',
+ ],
+ deps = [
+ ':usb',
+ '//motors:util',
+ ],
+ restricted_to = mcu_cpus,
+)
diff --git a/motors/usb/cdc.cc b/motors/usb/cdc.cc
index 0b59aef..5136937 100644
--- a/motors/usb/cdc.cc
+++ b/motors/usb/cdc.cc
@@ -167,11 +167,12 @@
}
{
- const auto abstract_control_management =
- CreateDescriptor(cdc_descriptor_subtype::abstract_control_management_length(),
- UsbClassDescriptorType::kInterface);
+ const auto abstract_control_management = CreateDescriptor(
+ cdc_descriptor_subtype::abstract_control_management_length(),
+ UsbClassDescriptorType::kInterface);
abstract_control_management->AddByte(
- cdc_descriptor_subtype::abstract_control_management()); // bDescriptorSubtype
+ cdc_descriptor_subtype::
+ abstract_control_management()); // bDescriptorSubtype
// We support:
// line_coding and serial_state
// send_break
@@ -198,7 +199,7 @@
m_endpoint_address_in()); // bEndpointAddress
endpoint_descriptor->AddByte(
m_endpoint_attributes_interrupt()); // bmAttributes
- endpoint_descriptor->AddUint16(kStatusMaxPacketSize); // wMaxEndpointSize
+ endpoint_descriptor->AddUint16(kStatusMaxPacketSize); // wMaxPacketSize
// Set it to the max because we have nothing to send, so no point using bus
// bandwidth asking.
endpoint_descriptor->AddByte(255); // bInterval
@@ -225,7 +226,7 @@
endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
endpoint_descriptor->AddByte(data_rx_endpoint_); // bEndpointAddress
endpoint_descriptor->AddByte(m_endpoint_attributes_bulk()); // bmAttributes
- endpoint_descriptor->AddUint16(kDataMaxPacketSize); // wMaxEndpointSize
+ endpoint_descriptor->AddUint16(kDataMaxPacketSize); // wMaxPacketSize
endpoint_descriptor->AddByte(1); // bInterval
}
@@ -235,7 +236,7 @@
endpoint_descriptor->AddByte(data_tx_endpoint_ |
m_endpoint_address_in()); // bEndpointAddress
endpoint_descriptor->AddByte(m_endpoint_attributes_bulk()); // bmAttributes
- endpoint_descriptor->AddUint16(kDataMaxPacketSize); // wMaxEndpointSize
+ endpoint_descriptor->AddUint16(kDataMaxPacketSize); // wMaxPacketSize
endpoint_descriptor->AddByte(1); // bInterval
}
}
@@ -365,8 +366,8 @@
device()->ConfigureEndpointFor(status_endpoint_, false, true, true);
} else if (endpoint == data_tx_endpoint_) {
device()->ConfigureEndpointFor(data_tx_endpoint_, false, true, true);
- next_tx_toggle_ = Data01::kData0;
DisableInterrupts disable_interrupts;
+ next_tx_toggle_ = Data01::kData0;
EnqueueTxData(disable_interrupts);
} else if (endpoint == data_rx_endpoint_) {
device()->ConfigureEndpointFor(data_rx_endpoint_, true, false, true);
diff --git a/motors/usb/cdc.h b/motors/usb/cdc.h
index f7c7437..4f5aa33 100644
--- a/motors/usb/cdc.h
+++ b/motors/usb/cdc.h
@@ -63,9 +63,6 @@
// for it.
static constexpr uint16_t kStatusMaxPacketSize = 1;
- ::std::array<::std::array<char, kDataMaxPacketSize>, 2> tx_buffers_,
- rx_buffers_;
-
void Initialize() override;
SetupResponse HandleEndpoint0SetupPacket(
@@ -99,6 +96,9 @@
void EnqueueTxData(const DisableInterrupts &);
+ ::std::array<::std::array<char, kDataMaxPacketSize>, 2> tx_buffers_,
+ rx_buffers_;
+
// In theory, we could sent notifications over this about things like the line
// state, but we don't have anything to report so we pretty much just ignore
// this.
diff --git a/motors/usb/hid.cc b/motors/usb/hid.cc
new file mode 100644
index 0000000..24ffe24
--- /dev/null
+++ b/motors/usb/hid.cc
@@ -0,0 +1,247 @@
+#include "motors/usb/hid.h"
+
+namespace frc971 {
+namespace teensy {
+namespace {
+
+constexpr uint8_t hid_class() { return 0x03; }
+
+namespace hid_class_requests {
+constexpr uint8_t get_report() { return 0x01; }
+constexpr uint8_t get_idle() { return 0x02; }
+constexpr uint8_t get_protocol() { return 0x03; }
+constexpr uint8_t set_report() { return 0x09; }
+constexpr uint8_t set_idle() { return 0x0a; }
+constexpr uint8_t set_protcol() { return 0x0b; }
+} // namespace hid_class_requests
+
+// The hard-coded HID report descriptor.
+uint8_t kReportDescriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop),
+ 0x09, 0x04, // Usage (Joystick),
+ 0xA1, 0x01, // Collection (Application),
+ 0x75, 0x10, // Report Size (16),
+ 0x95, 0x04, // Report Count (4),
+ 0x15, 0x00, // Logical Minimum (0),
+ 0x26, 0xFF, 0xFF, // Logical Maximum (65535),
+ 0x35, 0x00, // Physical Minimum (0),
+ 0x46, 0xFF, 0xFF, // Physical Maximum (65535),
+ 0x09, 0x30, // Usage (X),
+ 0x09, 0x31, // Usage (Y),
+ 0x09, 0x32, // Usage (Z),
+ 0x09, 0x35, // Usage (Rz),
+ 0x81, 0x02, // Input (Variable),
+ 0x75, 0x01, // Report Size (1),
+ 0x95, 0x10, // Report Count (16),
+ 0x25, 0x01, // Logical Maximum (1),
+ 0x45, 0x01, // Physical Maximum (1),
+ 0x05, 0x09, // Usage Page (Button),
+ 0x19, 0x01, // Usage Minimum (1),
+ 0x29, 0x10, // Usage Maximum (16),
+ 0x81, 0x02, // Input (Variable),
+ 0xC0 // End Collection
+};
+
+// The hard-coded HID descriptor.
+uint8_t kHidDescriptor[] = {
+ 9, // bLength
+ static_cast<uint8_t>(UsbClassDescriptorType::kHidHid), // bDescriptorType
+ 0x10, 0x01, // bcdHID
+ 0, // bCountryCode
+ 1, // bNumDescriptors
+ static_cast<uint8_t>(
+ UsbClassDescriptorType::kHidReport), // bDescriptorType
+ sizeof(kReportDescriptor), // wDescriptorLength
+ 0,
+};
+
+} // namespace
+
+void HidFunction::Initialize() {
+ interface_ = AddInterface();
+ in_endpoint_ = AddEndpoint();
+
+ {
+ const auto iad_descriptor = CreateDescriptor(
+ iad_descriptor_length(), UsbDescriptorType::kInterfaceAssociation);
+ iad_descriptor->AddByte(interface_); // bFirstInterface
+ iad_descriptor->AddByte(1); // bInterfaceCount
+ iad_descriptor->AddByte(hid_class()); // bFunctionClass
+ iad_descriptor->AddByte(0); // bFunctionSubClass
+ iad_descriptor->AddByte(0); // bFunctionProtocol
+ iad_descriptor->AddByte(device()->AddString("HidIad")); // iFunction
+ }
+
+ {
+ const auto interface_descriptor = CreateDescriptor(
+ interface_descriptor_length(), UsbDescriptorType::kInterface);
+ interface_descriptor->AddByte(interface_); // bInterfaceNumber
+ interface_descriptor->AddByte(0); // bAlternateSetting
+ interface_descriptor->AddByte(1); // bNumEndpoints
+ interface_descriptor->AddByte(hid_class()); // bInterfaceClass
+ interface_descriptor->AddByte(0); // bInterfaceSubClass
+ interface_descriptor->AddByte(0); // bInterfaceProtocol
+ interface_descriptor->AddByte(device()->AddString("Hid")); // iInterface
+ }
+
+ AddPremadeDescriptor(kHidDescriptor, sizeof(kHidDescriptor));
+
+ {
+ const auto endpoint_descriptor = CreateDescriptor(
+ endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
+ endpoint_descriptor->AddByte(in_endpoint_ |
+ m_endpoint_address_in()); // bEndpointAddress
+ endpoint_descriptor->AddByte(
+ m_endpoint_attributes_interrupt()); // bmAttributes
+ endpoint_descriptor->AddUint16(in_endpoint_max_size()); // wMaxPacketSize
+ endpoint_descriptor->AddByte(1); // bInterval
+ }
+}
+
+UsbFunction::SetupResponse HidFunction::HandleEndpoint0SetupPacket(
+ const UsbDevice::SetupPacket &setup_packet) {
+ if (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type) !=
+ SetupRequestType::kClass) {
+ return SetupResponse::kIgnored;
+ }
+ if (G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type) !=
+ standard_setup_recipients::kInterface) {
+ return SetupResponse::kIgnored;
+ }
+ if (setup_packet.index != interface_) {
+ return SetupResponse::kIgnored;
+ }
+ const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN;
+ switch (setup_packet.request) {
+ case hid_class_requests::get_report():
+ if (!in) {
+ return SetupResponse::kStall;
+ }
+ // If it's not requesting the only Input report, no idea what the host
+ // wants so stall.
+ if (setup_packet.value != 0x0100) {
+ return SetupResponse::kStall;
+ }
+ {
+ DisableInterrupts disable_interrupts;
+ memcpy(get_report_response_buffer_.data(),
+ report_tx_buffer_being_sent(disable_interrupts), kMaxReportSize);
+ }
+ device()->QueueEndpoint0Data(
+ reinterpret_cast<const char *>(get_report_response_buffer_.data()),
+ ::std::min<uint16_t>(setup_packet.length, report_max_size_));
+ return SetupResponse::kHandled;
+
+ case hid_class_requests::set_idle():
+ // Minimum implementation to make the host stack happy.
+ if (in) {
+ return SetupResponse::kStall;
+ }
+ device()->SendEmptyEndpoint0Packet();
+ return SetupResponse::kHandled;
+
+ // TODO(Brian): Should we actually implement the idle stuff?
+
+ default:
+ return SetupResponse::kStall;
+ }
+}
+
+UsbFunction::SetupResponse HidFunction::HandleGetDescriptor(
+ const UsbDevice::SetupPacket &setup_packet) {
+ const uint8_t recipient =
+ G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type);
+ if (recipient != standard_setup_recipients::kInterface) {
+ return SetupResponse::kIgnored;
+ }
+
+ const uint8_t descriptor_type = (setup_packet.value >> 8) & 0xFF;
+ if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type) !=
+ standard_descriptor_type_types::kClass) {
+ return SetupResponse::kIgnored;
+ }
+ if (setup_packet.index != interface_) {
+ return SetupResponse::kIgnored;
+ }
+
+ const uint8_t descriptor_index = setup_packet.value & 0xFF;
+ switch (descriptor_type) {
+ case static_cast<uint8_t>(UsbClassDescriptorType::kHidHid):
+ if (descriptor_index != 0) {
+ return SetupResponse::kStall;
+ }
+ device()->QueueEndpoint0Data(
+ reinterpret_cast<const char *>(kHidDescriptor),
+ ::std::min<int>(setup_packet.length, sizeof(kHidDescriptor)));
+ return SetupResponse::kHandled;
+
+ case static_cast<uint8_t>(UsbClassDescriptorType::kHidReport):
+ if (descriptor_index != 0) {
+ return SetupResponse::kStall;
+ }
+ device()->QueueEndpoint0Data(
+ reinterpret_cast<const char *>(kReportDescriptor),
+ ::std::min<int>(setup_packet.length, sizeof(kReportDescriptor)));
+ return SetupResponse::kHandled;
+
+ case static_cast<uint8_t>(UsbClassDescriptorType::kHidPhysical):
+ static constexpr char kNoPhysicalDescriptors[] = {0, 0, 0};
+ device()->QueueEndpoint0Data(
+ kNoPhysicalDescriptors,
+ ::std::min<int>(setup_packet.length, sizeof(kNoPhysicalDescriptors)));
+ return SetupResponse::kHandled;
+ }
+ return SetupResponse::kStall;
+}
+
+void HidFunction::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/,
+ EvenOdd odd) {
+ if (endpoint == in_endpoint_) {
+ DisableInterrupts disable_interrupts;
+ if (odd != BufferStateToEmpty(tx_state_)) {
+ __builtin_trap();
+ }
+
+ // Copy the current one into the just-sent buffer.
+ memcpy(report_tx_buffer_being_sent(disable_interrupts),
+ report_tx_buffer_to_fill(disable_interrupts), kMaxReportSize);
+
+ dma_memory_barrier();
+ device()->SetBdtEntry(
+ in_endpoint_, Direction::kTx, BufferStateToFill(tx_state_),
+ {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) |
+ static_cast<uint32_t>(next_tx_toggle_),
+ report_tx_buffer_to_fill(disable_interrupts)});
+
+ // Advance the state to indicate we've swapped buffers.
+ tx_state_ = BufferStateAfterFill(BufferStateAfterEmpty(tx_state_));
+ next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
+ }
+}
+
+void HidFunction::HandleConfigured(int endpoint) {
+ if (endpoint == in_endpoint_) {
+ device()->ConfigureEndpointFor(in_endpoint_, false, true, true);
+ DisableInterrupts disable_interrupts;
+ next_tx_toggle_ = Data01::kData0;
+
+ EvenOdd to_fill;
+ if (BufferStateHasFull(tx_state_)) {
+ to_fill = BufferStateToEmpty(tx_state_);
+ } else {
+ to_fill = BufferStateToFill(tx_state_);
+ tx_state_ = BufferStateAfterFill(tx_state_);
+ }
+
+ dma_memory_barrier();
+ device()->SetBdtEntry(
+ in_endpoint_, Direction::kTx, to_fill,
+ {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) |
+ static_cast<uint32_t>(next_tx_toggle_),
+ report_tx_buffer_to_fill(disable_interrupts)});
+ next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
+ }
+}
+
+} // namespace teensy
+} // namespace frc971
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_
diff --git a/motors/usb/usb.cc b/motors/usb/usb.cc
index 0a05fad..fb7fe9f 100644
--- a/motors/usb/usb.cc
+++ b/motors/usb/usb.cc
@@ -641,10 +641,30 @@
break;
case standard_setup_requests::kGetDescriptor:
- if (!in || recipient != standard_setup_recipients::kDevice) {
+ if (!in) {
break;
}
+
const uint8_t descriptor_type_byte = (setup_packet.value >> 8) & 0xFF;
+ if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type_byte) !=
+ standard_descriptor_type_types::kStandard) {
+ for (UsbFunction *function : functions_) {
+ switch (function->HandleGetDescriptor(setup_packet)) {
+ case SetupResponse::kIgnored:
+ continue;
+ case SetupResponse::kHandled:
+ return;
+ case SetupResponse::kStall:
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ if (recipient != standard_setup_recipients::kDevice) {
+ break;
+ }
if (descriptor_type_byte < kUsbDescriptorTypeMin ||
descriptor_type_byte > kUsbDescriptorTypeMax) {
break;
@@ -674,7 +694,8 @@
return;
case UsbDescriptorType::kString:
- if (descriptor_index != 0 && setup_packet.index != english_us_code()) {
+ if (descriptor_index != 0 &&
+ setup_packet.index != english_us_code()) {
break;
}
if (descriptor_index >= strings_.size()) {
diff --git a/motors/usb/usb.h b/motors/usb/usb.h
index 8b2cc67..4db29dc 100644
--- a/motors/usb/usb.h
+++ b/motors/usb/usb.h
@@ -2,6 +2,7 @@
#define MOTORS_USB_USB_H_
#include <assert.h>
+#include <string.h>
#include <string>
#include <vector>
#include <memory>
@@ -76,6 +77,10 @@
kString = 0x23,
kInterface = 0x24,
kEndpoint = 0x25,
+
+ kHidHid = 0x21,
+ kHidReport = 0x22,
+ kHidPhysical = 0x23,
};
// The names of the setup request types from the standard.
@@ -101,6 +106,16 @@
constexpr int kOther = 3;
} // namespace standard_setup_recipients
+// The HID class specification says this. Can't find any mention in the main
+// standard.
+#define G_DESCRIPTOR_TYPE_TYPE(descriptor_type) \
+ ((descriptor_type) >> 5 & UINT8_C(3))
+namespace standard_descriptor_type_types {
+constexpr int kStandard = 0;
+constexpr int kClass = 1;
+constexpr int kVendor = 2;
+} // namespace standard_descriptor_type_types
+
class UsbFunction;
// Allows building up a list of descriptors. This supports a much nicer API than
@@ -185,6 +200,13 @@
return CreateDescriptor(length, static_cast<uint8_t>(descriptor_type));
}
+ void AddPremadeDescriptor(const uint8_t *data, int length) {
+ const int start_index = data_.size();
+ const int end_index = start_index + length;
+ data_.resize(end_index);
+ memcpy(&data_[start_index], data, length);
+ }
+
void CheckFinished() const { assert(open_descriptors_ == 0); }
int CurrentSize() const { return data_.size(); }
@@ -469,6 +491,9 @@
return device_->config_descriptor_list_.CreateDescriptor(length,
descriptor_type);
}
+ void AddPremadeDescriptor(const uint8_t *data, int length) {
+ device_->config_descriptor_list_.AddPremadeDescriptor(data, length);
+ }
UsbDevice *device() const { return device_; }
@@ -485,6 +510,11 @@
return SetupResponse::kIgnored;
}
+ virtual SetupResponse HandleGetDescriptor(
+ const UsbDevice::SetupPacket & /*setup_packet*/) {
+ return SetupResponse::kIgnored;
+ }
+
virtual void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) = 0;
virtual void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
EvenOdd odd) = 0;