// Copyright 2025 Google Inc. // Use of this source code is governed by an Apache License that can be found // in the LICENSE file. #include "usb_device.h" #include #include #include #include "hardware/irq.h" #include "hardware/regs/usb.h" #include "hardware/resets.h" #include "hardware/structs/usb.h" #include "pico/types.h" namespace { constexpr uint8_t kControlEndpoint = 0u; UsbDevice* sUsbDevice = nullptr; } // namespace extern "C" void isr_usbctrl(void) { UsbDevice::HandleInterrupt(); } // static void UsbDevice::HandleInterrupt() { uint32_t status = usb_hw->ints; if (status & USB_INTS_SETUP_REQ_BITS) { static_cast(hw_clear_alias_untyped(usb_hw))->sie_status = USB_SIE_STATUS_SETUP_REC_BITS; sUsbDevice->HandleSetupRequest(); } if (status & USB_INTS_BUFF_STATUS_BITS) { sUsbDevice->HandleBufferStatus(); } if (status & USB_INTS_BUS_RESET_BITS) { static_cast(hw_clear_alias_untyped(usb_hw))->sie_status = USB_SIE_STATUS_BUS_RESET_BITS; sUsbDevice->HandleBusReset(); } } UsbDevice::UsbDevice( const DeviceDescriptor& device_descriptor, const ConfigurationDescriptor& configuration_descriptor, const std::vector& interface_descriptors, const std::vector& endpoint_descriptors, const std::vector& strings) : device_descriptor_(device_descriptor), configuration_descriptor_(configuration_descriptor), interface_descriptors_(interface_descriptors), endpoint_descriptors_(endpoint_descriptors), strings_(strings) { hard_assert(sUsbDevice == nullptr); sUsbDevice = this; reset_unreset_block_num_wait_blocking(RESET_USBCTRL); memset(usb_dpram, 0, sizeof(*usb_dpram)); irq_set_enabled(USBCTRL_IRQ, true); usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS; usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS; sending_data_.push_back(std::span()); receiving_data_.push_back(std::span()); uint32_t dpram_offset = offsetof(usb_device_dpram_t, epx_data); for (size_t i = 0; i < endpoint_descriptors_.size(); ++i) { uint32_t control = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | (endpoint_descriptors_[i].bmAttributes << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; if (endpoint_descriptors_[i].bEndpointAddress & kDirIn) { usb_dpram->ep_ctrl[i].in = control; } else { usb_dpram->ep_ctrl[i].out = control; } dpram_offset += endpoint_descriptors_[i].wMaxPacketSize; sending_data_.push_back(std::span()); receiving_data_.push_back(std::span()); } size_t endpoints = sending_data_.size(); in_next_pid_.resize(endpoints); out_next_pid_.resize(endpoints); static_cast(hw_set_alias_untyped(usb_hw))->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS; } void UsbDevice::FillConfigurations(std::vector& buffer) { buffer.clear(); buffer.reserve(configuration_descriptor_.wTotalLength); std::span config_span( reinterpret_cast(&configuration_descriptor_), sizeof(configuration_descriptor_)); buffer.insert(buffer.end(), config_span.begin(), config_span.end()); for (const auto& interface_descriptor : interface_descriptors_) { std::span interface_span( reinterpret_cast(&interface_descriptor), sizeof(interface_descriptor)); buffer.insert(buffer.end(), interface_span.begin(), interface_span.end()); } for (const auto& endpoint_descriptor : endpoint_descriptors_) { std::span endpoint_span( reinterpret_cast(&endpoint_descriptor), sizeof(endpoint_descriptor)); buffer.insert(buffer.end(), endpoint_span.begin(), endpoint_span.end()); } } void UsbDevice::GetDescriptor(volatile SetupPacket* setup) { uint8_t type = setup->wValue >> 8; switch (type) { case kDescriptorTypeDevice: Send(kControlEndpoint, std::span( reinterpret_cast(&device_descriptor_), std::min(sizeof(device_descriptor_), static_cast(setup->wLength)))); break; case kDescriptorTypeConfiguration: { FillConfigurations(setup_buffer_); Send(kControlEndpoint, std::span( setup_buffer_.data(), std::min(setup_buffer_.size(), static_cast(setup->wLength)))); break; } case kDescriptorTypeString: { uint8_t index = setup->wValue & 0xff; setup_buffer_.clear(); if (index == 0) { setup_buffer_.reserve(4); setup_buffer_.push_back(0x04); setup_buffer_.push_back(kDescriptorTypeString); setup_buffer_.push_back(0x09); setup_buffer_.push_back(0x04); } else if (index <= strings_.size()) { size_t size = strings_[index - 1].size() * 2 + 2; setup_buffer_.reserve(size); setup_buffer_.push_back(size); setup_buffer_.push_back(kDescriptorTypeString); for (size_t i = 0; i < strings_[index - 1].size(); ++i) { setup_buffer_.push_back(strings_[index - 1][i]); setup_buffer_.push_back(0); } } else { setup_buffer_.reserve(2); setup_buffer_.push_back(0x02); setup_buffer_.push_back(kDescriptorTypeString); } Send(kControlEndpoint, std::span( setup_buffer_.data(), std::min(setup_buffer_.size(), static_cast(setup->wLength)))); break; } default: printf("unsupported get descriptor: $%02x\n", type); break; } } // Note: better to return STALL on unsupported requests. void UsbDevice::HandleSetupRequest(volatile SetupPacket* setup) { uint8_t type = setup->bmRequestType & ~(kRecipientInterface | kRecipientEndPoint); if (type == kDirOut) { switch (setup->bRequest) { case kRequestClearFeature: AcknowledgeOutRequest(); break; case kRequestSetAddress: SetAddress(setup); break; case kRequestSetConfiguration: SetConfiguration(setup); break; default: printf("unsupported setup out: $%02x\n", setup->bRequest); break; } } else if (type == kDirIn) { switch (setup->bRequest) { case kRequestGetDescriptor: GetDescriptor(setup); break; default: printf("unsupported setup in: $%02x\n", setup->bRequest); break; } } } void UsbDevice::AcknowledgeOutRequest() { Send(kControlEndpoint, std::span({})); } void UsbDevice::Send(uint8_t endpoint, std::span data) { if (endpoint == kControlEndpoint) { in_next_pid_[endpoint] = 1u; } sending_data_[endpoint] = data; SendInternal(endpoint); } void UsbDevice::Receive(uint8_t endpoint, std::span data) { receiving_data_[endpoint] = data; out_next_pid_[endpoint] = 1u; ReceiveInternal(endpoint); } void UsbDevice::SendInternal(uint8_t endpoint) { size_t transfer_size = std::min(sending_data_[endpoint].size(), GetMaxPacketSize(endpoint)); uint8_t* buffer = GetTransferBuffer(endpoint); memcpy(buffer, sending_data_[endpoint].data(), transfer_size); uint32_t pid = in_next_pid_[endpoint] ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; in_next_pid_[endpoint] ^= 1u; usb_dpram->ep_buf_ctrl[endpoint].in = transfer_size | USB_BUF_CTRL_AVAIL | USB_BUF_CTRL_FULL | pid; size_t remaining_size = sending_data_[endpoint].size() - transfer_size; if (remaining_size) { sending_data_[endpoint] = std::span(&sending_data_[endpoint][transfer_size], remaining_size); } else { sending_data_[endpoint] = std::span(); } } void UsbDevice::ReceiveInternal(uint8_t endpoint) { size_t transfer_size = std::min(receiving_data_[endpoint].size(), GetMaxPacketSize(endpoint)); uint32_t pid = out_next_pid_[endpoint] ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; usb_dpram->ep_buf_ctrl[endpoint].out = transfer_size | USB_BUF_CTRL_AVAIL | pid; } void UsbDevice::HandleSetupRequest() { volatile SetupPacket* setup = reinterpret_cast(&usb_dpram->setup_packet); HandleSetupRequest(setup); } void UsbDevice::HandleBufferStatus() { uint32_t status = usb_hw->buf_status; uint8_t endpoint_bits = (1 + endpoint_descriptors_.size()) * 2; for (uint8_t i = 0; i < endpoint_bits; ++i) { uint32_t bit = 1 << i; uint8_t endpoint = i >> 1; bool out = i & 1; if ((status & bit) == 0) { continue; } static_cast(hw_clear_alias_untyped(usb_hw))->buf_status = bit; if (out) { OnReceived(endpoint, usb_dpram->ep_buf_ctrl[endpoint].out & USB_BUF_CTRL_LEN_MASK); } else { OnSent(endpoint, usb_dpram->ep_buf_ctrl[endpoint].in & USB_BUF_CTRL_LEN_MASK); } } } void UsbDevice::HandleBusReset() { address_ = 0; ready_ = false; usb_hw->dev_addr_ctrl = 0; should_set_address_ = false; for (auto& data : sending_data_) { data = std::span(); } for (auto& data : receiving_data_) { data = std::span(); } } void UsbDevice::OnSent(uint8_t endpoint, uint32_t length) { if (should_set_address_) { usb_hw->dev_addr_ctrl = address_; should_set_address_ = false; } else if (length == 0) { return; } else if (sending_data_[endpoint].empty()) { Receive(endpoint, {}); OnCompleteToSend(endpoint); } else { SendInternal(endpoint); } } void UsbDevice::OnReceived(uint8_t endpoint, uint32_t length) { if (length == 0) { return; } size_t receive_size = std::min(receiving_data_[endpoint].size(), static_cast(length)); uint8_t* buffer = GetTransferBuffer(endpoint); memcpy(receiving_data_[endpoint].data(), buffer, receive_size); size_t remaining_size = receiving_data_[endpoint].size() - receive_size; if (remaining_size) { receiving_data_[endpoint] = std::span(&receiving_data_[endpoint][receive_size], remaining_size); ReceiveInternal(endpoint); } else { receiving_data_[endpoint] = std::span(); Send(endpoint, {}); } } void UsbDevice::SetAddress(volatile SetupPacket* setup) { address_ = setup->wValue & 0xff; should_set_address_ = true; AcknowledgeOutRequest(); } void UsbDevice::SetConfiguration(volatile SetupPacket* setup) { ready_ = true; AcknowledgeOutRequest(); } size_t UsbDevice::GetMaxPacketSize(uint8_t endpoint) { if (endpoint == kControlEndpoint) { return device_descriptor_.bMaxPacketSize0; } else if (endpoint <= endpoint_descriptors_.size()) { return endpoint_descriptors_[endpoint - 1].wMaxPacketSize; } hard_assert(false); return 0; } uint8_t* UsbDevice::GetTransferBuffer(uint8_t endpoint) { if (endpoint == kControlEndpoint) { return usb_dpram->ep0_buf_a; } uint8_t* buffer = usb_dpram->epx_data; for (size_t i = 1; i < endpoint; ++i) { buffer += endpoint_descriptors_[i].wMaxPacketSize; } return buffer; }