Add mozc dial version.

Co-authored-by: Takashi Toyoshima <toyoshim@google.com>
Co-authored-by: Shun Ikejima <ikejima@google.com>
This commit is contained in:
Shun Ikejima
2025-10-01 10:01:00 +09:00
parent 9b4017fb9b
commit 6783bfea76
88 changed files with 233948 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#include "dial_controller.h"
#include <algorithm>
namespace {
constexpr uint8_t kBasePosition = 0;
uint8_t ConvertGrayToBinary(uint8_t gray) {
uint8_t binary = gray;
uint8_t mask = gray >> 1;
while (mask) {
binary ^= mask;
mask >>= 1;
}
return binary;
}
} // namespace
DialController::DialController() {}
void DialController::Update(uint8_t sensor_gray_code) {
position_ = ConvertGrayToBinary(sensor_gray_code);
max_position_ = std::max(max_position_, position_);
if (position_ == kBasePosition && max_position_ != kBasePosition) {
decided_position_ = max_position_;
max_position_ = kBasePosition;
}
}
bool DialController::IsBasePosition() const {
return position_ == kBasePosition;
}
std::optional<uint8_t> DialController::PopDecidedPosition() {
auto result = decided_position_;
decided_position_ = std::nullopt;
return result;
}

View File

@@ -0,0 +1,28 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_DIAL_CONTROLLER_H
#define COMMON_DIAL_CONTROLLER_H
#include <cstdint>
#include <optional>
class DialController {
public:
DialController();
DialController(const DialController&) = delete;
DialController& operator=(const DialController&) = delete;
~DialController() = default;
void Update(uint8_t sensor_gray_code);
bool IsBasePosition() const;
std::optional<uint8_t> PopDecidedPosition();
private:
uint8_t position_ = 0u;
uint8_t max_position_ = 0u;
std::optional<uint8_t> decided_position_;
};
#endif // COMMON_DIAL_CONTROLLER_H

View File

@@ -0,0 +1,108 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#include "motor_controller.h"
#include <cstdio>
#include <initializer_list>
#include "hardware/gpio.h"
#include "pico/time.h"
static constexpr int kMotorStepIntervalInUs = 2000;
// Activate 1 of 8 motor drivers in a specific step phase.
class MotorController::Decoder {
public:
Decoder(int8_t a0, int8_t a1, int8_t a2, int8_t en)
: a0_(a0), a1_(a1), a2_(a2), en_(en) {
for (int8_t gpio : {en, a0, a1, a2}) {
gpio_init(gpio);
gpio_set_dir(gpio, GPIO_OUT);
gpio_put(gpio, false);
}
}
Decoder(const Decoder&) = delete;
Decoder& operator=(const Decoder&) = delete;
~Decoder() = default;
void Select(uint8_t index) {
hard_assert(index < 8);
gpio_put(a0_, index & 1);
gpio_put(a1_, index & 2);
gpio_put(a2_, index & 4);
gpio_put(en_, true);
}
void Unselect() { gpio_put(en_, false); }
private:
int8_t a0_;
int8_t a1_;
int8_t a2_;
int8_t en_;
};
// static
bool MotorController::OnAlarm(repeating_timer* t) {
static_cast<MotorController*>(t->user_data)->Step();
return true;
}
MotorController::MotorController(Mode mode) {
if (mode == Mode::k9Motor) {
// Each decoder is responsible for a specific phase to driver 1 of 8 motor
// drivers. As we have 4 phases, it can drive 4 motors at the same time, and
// by time division multiplexing, we make them drive 8 motors.
decoder_[0] = std::make_unique<Decoder>(1, 2, 3, 4);
decoder_[1] = std::make_unique<Decoder>(5, 6, 7, 8);
decoder_[2] = std::make_unique<Decoder>(9, 10, 11, 12);
decoder_[3] = std::make_unique<Decoder>(13, 14, 15, 16);
}
// 9th motor driver is controlled via GPIOs directly.
for (int8_t gpio : {17, 18, 19, 20}) {
gpio_init(gpio);
gpio_set_dir(gpio, GPIO_OUT);
gpio_put(gpio, false);
}
add_repeating_timer_us(-kMotorStepIntervalInUs, OnAlarm, this, &timer_);
}
MotorController::~MotorController() {}
void MotorController::Start(uint8_t index) {
hard_assert(index < kNumOfMotors);
started_[index] = true;
}
void MotorController::Stop(uint8_t index) {
hard_assert(index < kNumOfMotors);
started_[index] = false;
}
void MotorController::Step() {
// Drive first 4 motor drivers in even phases, and later 4 ones in odd
// phases.
size_t start_index = (phase_ & 1) ? kNumOfPhases : 0;
size_t half_phase = phase_ >> 1;
for (size_t i = start_index; i < start_index + kNumOfPhases; ++i) {
if (!decoder_[i]) {
continue;
}
if (started_[i]) {
decoder_[(i + half_phase) % kNumOfPhases]->Select(i);
} else {
decoder_[(i + half_phase) % kNumOfPhases]->Unselect();
}
}
// Drive the last 9th motor drivers.
bool on = started_[8] && phase_ & 1;
gpio_put(17, on && half_phase == 0);
gpio_put(18, on && half_phase == 1);
gpio_put(19, on && half_phase == 2);
gpio_put(20, on && half_phase == 3);
phase_ = (phase_ + 7) % 8;
}

View File

@@ -0,0 +1,38 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_MOTOR_CONTROLLER_H_
#define COMMON_MOTOR_CONTROLLER_H_
#include <cstdint>
#include <memory>
#include "pico/time.h"
class MotorController final {
public:
enum class Mode { k1Motor, k9Motor };
explicit MotorController(Mode = Mode::k9Motor);
MotorController(const MotorController&) = delete;
MotorController& operator=(const MotorController&) = delete;
~MotorController();
void Start(uint8_t index);
void Stop(uint8_t index);
private:
class Decoder;
static bool OnAlarm(repeating_timer* t);
void Step();
static constexpr size_t kNumOfPhases = 4;
static constexpr size_t kNumOfMotors = 9;
std::unique_ptr<Decoder> decoder_[kNumOfPhases];
bool started_[kNumOfMotors] = { false };
uint8_t phase_ = 0;
repeating_timer timer_;
};
#endif // COMMON_MOTOR_CONTROLLER_H_

View File

@@ -0,0 +1,32 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#include "photo_sensor.h"
#include "hardware/gpio.h"
PhotoSensor::PhotoSensor(int8_t bit0,
int8_t bit1,
int8_t bit2,
int8_t bit3,
int8_t bit4,
int8_t bit5)
: bits({bit0, bit1, bit2, bit3, bit4, bit5}) {
for (int8_t gpio : bits) {
gpio_init(gpio);
gpio_set_dir(gpio, GPIO_IN);
gpio_pull_up(gpio); // Built-in 50-80K pull-up
}
}
uint8_t PhotoSensor::Read() {
uint8_t value = 0;
for (size_t bit = 0; bit < bits.size(); ++bit) {
if (bits[bit] == kNotUsed) {
break;
}
value |= gpio_get(bits[bit]) ? (1 << bit) : 0;
}
return value;
}

View File

@@ -0,0 +1,33 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_PHOTO_SENSOR_H_
#define COMMON_PHOTO_SENSOR_H_
#include <array>
#include <cstddef>
#include <cstdint>
class PhotoSensor final {
public:
PhotoSensor(int8_t bit0,
int8_t bit1 = kNotUsed,
int8_t bit2 = kNotUsed,
int8_t bit3 = kNotUsed,
int8_t bit4 = kNotUsed,
int8_t bit5 = kNotUsed);
PhotoSensor(const PhotoSensor&) = delete;
PhotoSensor& operator=(const PhotoSensor&) = delete;
~PhotoSensor() = default;
uint8_t Read();
private:
static constexpr int8_t kNotUsed = -1;
static constexpr size_t kMaxWidth = 6;
std::array<int8_t, kMaxWidth> bits;
};
#endif // COMMON_PHOTO_SENSOR_H_

View File

@@ -0,0 +1,145 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#include "usage_tables.h"
namespace usage_tables {
const std::vector<uint8_t> a = {
0x00, // no input (blank area between the base and the first position)
0x2f, // @
0x34, // :
0x87, // _
0x13, // p
0x33, // ;
0x38, // /
0x12, // o
0x0f, // l
0x37, // .
0x0c, // i
0x0e, // k
0x36, // ,
0x18, // u
0x0d, // j
0x10, // m
0x1c, // y
0x0b, // h
0x11, // n
0x17, // t
0x0a, // g
0x05, // b
0x15, // r
0x09, // f
0x19, // v
0x08, // e
0x07, // d
0x06, // c
0x1a, // w
0x16, // s
0x1b, // x
0x14, // q
0x04, // a
0x1d, // z
0x39, // <CAPS>
0x2c, // <SPACE>
};
const std::vector<uint8_t> fn_a = {};
const std::vector<uint8_t> modifier_a = {};
const std::vector<uint8_t> b = {
0x2e, // ^ (~)
0x29, // <ESC>
0x2b, // <TAB>
};
const std::vector<uint8_t> fn_b = {};
const std::vector<uint8_t> modifier_b = {};
const std::vector<uint8_t> c = {
0x00, // <SHIFT>
0x00, // <CTRL>
0x00, // <ALT>
0x00, // <FN>
};
const std::vector<uint8_t> fn_c = {};
const std::vector<uint8_t> modifier_c = {
kModifierLeftShift,
kModifierLeftCtrl,
kModifierLeftAlt,
kModifierLeftGUI,
};
const std::vector<uint8_t> d = {
0x00, // no input
0x28, // <ENTER>
};
const std::vector<uint8_t> fn_d = {};
const std::vector<uint8_t> modifier_d = {};
const std::vector<uint8_t> e = {
0x00, // no input
0x4d, // <END>
0x4e, // <PAGE DOWN>
0x4b, // <PAGE UP>
0x4a, // <HOME>
0x49, // <INS>
0x4c, // <DEL>
};
const std::vector<uint8_t> fn_e = {};
const std::vector<uint8_t> modifier_e = {};
const std::vector<uint8_t> f = {
0x00, // no input
0x4f, // <RIGHT>
0x52, // <UP>
0x50, // <LEFT>
0x51, // <DOWN>
};
const std::vector<uint8_t> fn_f = {};
const std::vector<uint8_t> modifier_f = {};
const std::vector<uint8_t> g = {
0x55, // KP-*
0x56, // KP-/
0x63, // KP-.
};
const std::vector<uint8_t> fn_g = {};
const std::vector<uint8_t> modifier_g = {};
const std::vector<uint8_t> h = {
0x57, // KP-+
0x56, // KP--
0x67, // KP-=
};
const std::vector<uint8_t> fn_h = {};
const std::vector<uint8_t> modifier_h = {};
const std::vector<uint8_t> i = {
0x00, // no input
0x26, // 9
0x25, // 8
0x24, // 7
0x23, // 6
0x22, // 5
0x21, // 4
0x20, // 3
0x1f, // 2
0x1e, // 1
0x27, // 0
};
const std::vector<uint8_t> fn_i = {
0x00, // no input
0x42, // F9
0x41, // F8
0x40, // F7
0x3f, // F6
0x3e, // F5
0x3d, // F4
0x3c, // F3
0x3b, // F2
0x3a, // F1
0x43, // F10
};
const std::vector<uint8_t> modifier_i = {};
} // namespace usage_tables

View File

@@ -0,0 +1,54 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_USAGE_TABLES_
#define COMMON_USAGE_TABLES_
#include <vector>
#include <cstdint>
namespace usage_tables {
static constexpr uint8_t kModifierLeftCtrl = (1 << 0);
static constexpr uint8_t kModifierLeftShift = (1 << 1);
static constexpr uint8_t kModifierLeftAlt = (1 << 2);
static constexpr uint8_t kModifierLeftGUI = (1 << 3);
static constexpr uint8_t kModifierRightCtrl = (1 << 4);
static constexpr uint8_t kModifierRightShift = (1 << 5);
static constexpr uint8_t kModifierRightAlt = (1 << 6);
static constexpr uint8_t kModifierRightGUI = (1 << 7);
extern const std::vector<uint8_t> a;
extern const std::vector<uint8_t> b;
extern const std::vector<uint8_t> c;
extern const std::vector<uint8_t> d;
extern const std::vector<uint8_t> e;
extern const std::vector<uint8_t> f;
extern const std::vector<uint8_t> g;
extern const std::vector<uint8_t> h;
extern const std::vector<uint8_t> i;
extern const std::vector<uint8_t> fn_a;
extern const std::vector<uint8_t> fn_b;
extern const std::vector<uint8_t> fn_c;
extern const std::vector<uint8_t> fn_d;
extern const std::vector<uint8_t> fn_e;
extern const std::vector<uint8_t> fn_f;
extern const std::vector<uint8_t> fn_g;
extern const std::vector<uint8_t> fn_h;
extern const std::vector<uint8_t> fn_i;
extern const std::vector<uint8_t> modifier_a;
extern const std::vector<uint8_t> modifier_b;
extern const std::vector<uint8_t> modifier_c;
extern const std::vector<uint8_t> modifier_d;
extern const std::vector<uint8_t> modifier_e;
extern const std::vector<uint8_t> modifier_f;
extern const std::vector<uint8_t> modifier_g;
extern const std::vector<uint8_t> modifier_h;
extern const std::vector<uint8_t> modifier_i;
} // namespace usage_tables
#endif // COMMON_USAGE_TABLES_

View File

@@ -0,0 +1,353 @@
// 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 <cstdio>
#include <cstring>
#include <memory>
#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<usb_hw_t*>(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<usb_hw_t*>(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<InterfaceDescriptor>& interface_descriptors,
const std::vector<EndPointDescriptor>& endpoint_descriptors,
const std::vector<std::string>& 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<const uint8_t>());
receiving_data_.push_back(std::span<uint8_t>());
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<const uint8_t>());
receiving_data_.push_back(std::span<uint8_t>());
}
size_t endpoints = sending_data_.size();
in_next_pid_.resize(endpoints);
out_next_pid_.resize(endpoints);
static_cast<usb_hw_t*>(hw_set_alias_untyped(usb_hw))->sie_ctrl =
USB_SIE_CTRL_PULLUP_EN_BITS;
}
void UsbDevice::FillConfigurations(std::vector<uint8_t>& buffer) {
buffer.clear();
buffer.reserve(configuration_descriptor_.wTotalLength);
std::span<const uint8_t> config_span(
reinterpret_cast<const uint8_t*>(&configuration_descriptor_),
sizeof(configuration_descriptor_));
buffer.insert(buffer.end(), config_span.begin(), config_span.end());
for (const auto& interface_descriptor : interface_descriptors_) {
std::span<const uint8_t> interface_span(
reinterpret_cast<const uint8_t*>(&interface_descriptor),
sizeof(interface_descriptor));
buffer.insert(buffer.end(), interface_span.begin(), interface_span.end());
}
for (const auto& endpoint_descriptor : endpoint_descriptors_) {
std::span<const uint8_t> endpoint_span(
reinterpret_cast<const uint8_t*>(&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<const uint8_t>(
reinterpret_cast<const uint8_t*>(&device_descriptor_),
std::min(sizeof(device_descriptor_),
static_cast<size_t>(setup->wLength))));
break;
case kDescriptorTypeConfiguration: {
FillConfigurations(setup_buffer_);
Send(kControlEndpoint, std::span<const uint8_t>(
setup_buffer_.data(),
std::min(setup_buffer_.size(),
static_cast<size_t>(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<const uint8_t>(
setup_buffer_.data(),
std::min(setup_buffer_.size(),
static_cast<size_t>(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<uint8_t>({}));
}
void UsbDevice::Send(uint8_t endpoint, std::span<const uint8_t> data) {
if (endpoint == kControlEndpoint) {
in_next_pid_[endpoint] = 1u;
}
sending_data_[endpoint] = data;
SendInternal(endpoint);
}
void UsbDevice::Receive(uint8_t endpoint, std::span<uint8_t> 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<const uint8_t>();
}
}
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<volatile SetupPacket*>(&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<usb_hw_t*>(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<const uint8_t>();
}
for (auto& data : receiving_data_) {
data = std::span<uint8_t>();
}
}
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<size_t>(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<uint8_t>();
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;
}

View File

@@ -0,0 +1,145 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_USB_DEVICE_H_
#define COMMON_USB_DEVICE_H_
#include <cstdint>
#include <span>
#include <string>
#include <vector>
class UsbDevice {
public:
struct SetupPacket {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__((packed));
struct DeviceDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} __attribute__((packed));
struct ConfigurationDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __attribute__((packed));
struct InterfaceDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} __attribute__((packed));
struct EndPointDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} __attribute__((packed));
static constexpr uint8_t kDirOut = 0x00u;
static constexpr uint8_t kDirIn = 0x80u;
static constexpr uint8_t kTypeClass = 0x20;
static constexpr uint8_t kRecipientInterface = 0x01;
static constexpr uint8_t kRecipientEndPoint = 0x02;
static constexpr uint8_t kRequestClearFeature = 0x01u;
static constexpr uint8_t kRequestSetAddress = 0x05u;
static constexpr uint8_t kRequestGetDescriptor = 0x06u;
static constexpr uint8_t kRequestSetConfiguration = 0x09u;
static constexpr uint8_t kDescriptorTypeDevice = 0x01u;
static constexpr uint8_t kDescriptorTypeConfiguration = 0x02u;
static constexpr uint8_t kDescriptorTypeString = 0x03u;
static constexpr uint8_t kDescriptorTypeInterface = 0x04u;
static constexpr uint8_t kDescriptorTypeEndPoint = 0x05u;
static constexpr uint8_t kEndPointAttributeInterrupt = 0x03u;
static void HandleInterrupt();
UsbDevice(const DeviceDescriptor& device_descriptor,
const ConfigurationDescriptor& configuration_descriptor,
const std::vector<InterfaceDescriptor>& interface_descriptors,
const std::vector<EndPointDescriptor>& endpoint_descriptors,
const std::vector<std::string>& strings);
UsbDevice(const UsbDevice&) = delete;
UsbDevice& operator=(const UsbDevice&) = delete;
virtual ~UsbDevice() = default;
protected:
virtual void FillConfigurations(std::vector<uint8_t>& buffer);
virtual void GetDescriptor(volatile SetupPacket* setup);
virtual void HandleSetupRequest(volatile SetupPacket* setup);
virtual void OnCompleteToSend(uint8_t endpoint) {};
void AcknowledgeOutRequest();
void Send(uint8_t endpoint, std::span<const uint8_t> data);
void Receive(uint8_t endpoint, std::span<uint8_t> data);
private:
void HandleSetupRequest();
void HandleBufferStatus();
void HandleBusReset();
void SendInternal(uint8_t endpoint);
void ReceiveInternal(uint8_t endpoint);
void OnSent(uint8_t endpoint, uint32_t length);
void OnReceived(uint8_t endpoint, uint32_t length);
void SetAddress(volatile SetupPacket*);
void SetConfiguration(volatile SetupPacket*);
size_t GetMaxPacketSize(uint8_t endpoint);
uint8_t* GetTransferBuffer(uint8_t endpoint);
const DeviceDescriptor& device_descriptor_;
const ConfigurationDescriptor& configuration_descriptor_;
const std::vector<InterfaceDescriptor>& interface_descriptors_;
const std::vector<EndPointDescriptor>& endpoint_descriptors_;
const std::vector<std::string>& strings_;
uint8_t address_ = 0;
bool should_set_address_ = false;
bool ready_ = false;
std::vector<uint8_t> in_next_pid_ = {0u};
std::vector<uint8_t> out_next_pid_ = {0u};
std::vector<uint8_t> setup_buffer_;
std::vector<std::span<const uint8_t>> sending_data_;
std::vector<std::span<uint8_t>> receiving_data_;
};
#endif // COMMON_USB_DEVICE_H_

View File

@@ -0,0 +1,94 @@
// 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_hid_device.h"
UsbHidDevice::UsbHidDevice(
const DeviceDescriptor& device_descriptor,
const ConfigurationDescriptor& configuration_descriptor,
const std::vector<InterfaceDescriptor>& interface_descriptors,
const std::vector<EndPointDescriptor>& endpoint_descriptors,
const HidDescriptor& hid_descriptor,
const std::vector<uint8_t>& report_descriptor,
const std::vector<std::string>& strings)
: UsbDevice(device_descriptor,
configuration_descriptor,
interface_descriptors,
endpoint_descriptors,
strings),
configuration_descriptor_(configuration_descriptor),
interface_descriptors_(interface_descriptors),
endpoint_descriptors_(endpoint_descriptors),
hid_descriptor_(hid_descriptor),
report_descriptor_(report_descriptor) {}
void UsbHidDevice::Report(uint8_t endpoint, std::span<const uint8_t> report) {
Send(endpoint, report);
}
void UsbHidDevice::FillConfigurations(std::vector<uint8_t>& buffer) {
buffer.clear();
buffer.reserve(configuration_descriptor_.wTotalLength);
std::span<const uint8_t> config_span(
reinterpret_cast<const uint8_t*>(&configuration_descriptor_),
sizeof(configuration_descriptor_));
buffer.insert(buffer.end(), config_span.begin(), config_span.end());
for (const auto& interface_descriptor : interface_descriptors_) {
std::span<const uint8_t> interface_span(
reinterpret_cast<const uint8_t*>(&interface_descriptor),
sizeof(interface_descriptor));
buffer.insert(buffer.end(), interface_span.begin(), interface_span.end());
}
std::span<const uint8_t> hid_span(
reinterpret_cast<const uint8_t*>(&hid_descriptor_),
sizeof(hid_descriptor_));
buffer.insert(buffer.end(), hid_span.begin(), hid_span.end());
for (const auto& endpoint_descriptor : endpoint_descriptors_) {
std::span<const uint8_t> endpoint_span(
reinterpret_cast<const uint8_t*>(&endpoint_descriptor),
sizeof(endpoint_descriptor));
buffer.insert(buffer.end(), endpoint_span.begin(), endpoint_span.end());
}
}
void UsbHidDevice::GetDescriptor(volatile SetupPacket* setup) {
uint8_t type = setup->wValue >> 8;
switch (type) {
case UsbHidDevice::kDescriptorTypeReport:
Send(0, std::span<const uint8_t>(report_descriptor_));
break;
default:
UsbDevice::GetDescriptor(setup);
break;
}
}
void UsbHidDevice::HandleSetupRequest(volatile SetupPacket* setup) {
uint8_t type =
setup->bmRequestType & ~(kRecipientInterface | kRecipientEndPoint);
if (type == (kDirOut | kTypeClass)) {
switch (setup->bRequest) {
case kHidRequestSetReport:
receive_buffer_.resize(setup->wLength);
Receive(0, receive_buffer_);
break;
case kHidRequestSetIdle:
AcknowledgeOutRequest();
break;
case kHidRequestSetProtocol:
printf("set protocol: %s\n", setup->wValue ? "report" : "boot");
AcknowledgeOutRequest();
break;
default:
AcknowledgeOutRequest();
printf("unsupported HID class setup out: $%02x\n", setup->bRequest);
break;
}
} else if (type == (kDirIn | kTypeClass)) {
AcknowledgeOutRequest();
printf("unsupported HID class setup in: $%02x\n", setup->bRequest);
} else {
UsbDevice::HandleSetupRequest(setup);
}
}

View File

@@ -0,0 +1,60 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_USB_HID_DEVICE_H_
#define COMMON_USB_HID_DEVICE_H_
#include "usb_device.h"
#include <cstdint>
#include <string>
#include <vector>
class UsbHidDevice : public UsbDevice {
public:
struct HidDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdHID;
uint8_t bCountryCode;
uint8_t bNumDescriptors;
uint8_t bReportDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed));
static constexpr uint8_t kHidRequestSetReport = 0x09u;
static constexpr uint8_t kHidRequestSetIdle = 0x0au;
static constexpr uint8_t kHidRequestSetProtocol = 0x0bu;
static constexpr uint8_t kDescriptorTypeHid = 0x21u;
static constexpr uint8_t kDescriptorTypeReport = 0x22u;
UsbHidDevice(const DeviceDescriptor& device_descriptor,
const ConfigurationDescriptor& configuration_descriptor,
const std::vector<InterfaceDescriptor>& interface_descriptors,
const std::vector<EndPointDescriptor>& endpoint_descriptors,
const HidDescriptor& hid_descriptor,
const std::vector<uint8_t>& report_descriptor,
const std::vector<std::string>& strings);
UsbHidDevice(const UsbHidDevice&) = delete;
UsbHidDevice& operator=(const UsbHidDevice&) = delete;
~UsbHidDevice() override = default;
void Report(uint8_t endpoint, std::span<const uint8_t> report);
private:
// Implement UsbDevice.
void FillConfigurations(std::vector<uint8_t>& buffer) override;
void GetDescriptor(volatile SetupPacket* setup_packet) override;
void HandleSetupRequest(volatile SetupPacket* setup_packet) override;
const ConfigurationDescriptor& configuration_descriptor_;
const std::vector<InterfaceDescriptor>& interface_descriptors_;
const std::vector<EndPointDescriptor>& endpoint_descriptors_;
const HidDescriptor& hid_descriptor_;
const std::vector<uint8_t>& report_descriptor_;
std::vector<uint8_t> receive_buffer_;
};
#endif // COMMON_USB_HID_DEVICE_H_

View File

@@ -0,0 +1,203 @@
// 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_hid_keyboard.h"
namespace {
constexpr uint8_t kManufacturerStringIndex = 1;
constexpr uint8_t kProductStringIndex = 2;
constexpr uint8_t kSerialNumberStringIndex = 3;
constexpr uint8_t kModifierReportIndex = 0;
constexpr uint8_t kKeycodeReportIndex = 2;
UsbDevice::DeviceDescriptor device_descriptor = {
.bLength = sizeof(UsbDevice::DeviceDescriptor),
.bDescriptorType = UsbDevice::kDescriptorTypeDevice,
.bcdUSB = 0x0110,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0,
.idProduct = 0,
.bcdDevice = 0,
.iManufacturer = kManufacturerStringIndex,
.iProduct = kProductStringIndex,
.iSerialNumber = kSerialNumberStringIndex,
.bNumConfigurations = 1};
std::vector<UsbDevice::EndPointDescriptor> endpoint_descriptors = {
{.bLength = sizeof(UsbDevice::EndPointDescriptor),
.bDescriptorType = UsbDevice::kDescriptorTypeEndPoint,
.bEndpointAddress = UsbDevice::kDirIn | 1,
.bmAttributes = UsbDevice::kEndPointAttributeInterrupt,
.wMaxPacketSize = 64,
.bInterval = 10},
{.bLength = sizeof(UsbDevice::EndPointDescriptor),
.bDescriptorType = UsbDevice::kDescriptorTypeEndPoint,
.bEndpointAddress = UsbDevice::kDirOut | 1,
.bmAttributes = UsbDevice::kEndPointAttributeInterrupt,
.wMaxPacketSize = 64,
.bInterval = 10}};
std::vector<UsbDevice::InterfaceDescriptor> interface_descriptors = {
{.bLength = sizeof(UsbDevice::InterfaceDescriptor),
.bDescriptorType = UsbDevice::kDescriptorTypeInterface,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = static_cast<uint8_t>(endpoint_descriptors.size()),
.bInterfaceClass = 3, // HID
.bInterfaceSubClass = 1, // Boot
.bInterfaceProtocol = 1, // Keyboard
.iInterface = 0}};
std::vector<uint8_t> report_descriptor = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x06, /* USAGE (Keyboard) */
0xa1, 0x01, /* COLLECTION (Application) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0xe0, /* USAGE_MINIMUM (224) */
0x29, 0xe7, /* USAGE_MAXIMUM (231) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x81, 0x02, /* INPUT (Data,Var,Abs); Modifier byte */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x81, 0x01, /* INPUT (Constant); Reserved byte */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x05, /* REPORT_COUNT (5) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x19, 0x01, /* USAGE_MINIMUM (1) */
0x29, 0x05, /* USAGE_MAXIMUM (5) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs); LED report */
0x75, 0x03, /* REPORT_SIZE (3) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x01, /* OUTPUT (Constant); LED report padding */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0x00, /* USAGE_MINIMUM (0) */
0x29, 0x65, /* USAGE_MAXIMUM (101) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
0xC0, /* END_COLLECTION */
};
UsbHidDevice::HidDescriptor hid_descriptor = {
.bLength = sizeof(UsbHidDevice::HidDescriptor),
.bDescriptorType = UsbHidDevice::kDescriptorTypeHid,
.bcdHID = 0x0110,
.bCountryCode = 0x00,
.bNumDescriptors = 1,
.bReportDescriptorType = UsbHidDevice::kDescriptorTypeReport,
.wDescriptorLength = static_cast<uint16_t>(report_descriptor.size())};
UsbDevice::ConfigurationDescriptor configuration_descriptor = {
.bLength = sizeof(configuration_descriptor),
.bDescriptorType = UsbDevice::kDescriptorTypeConfiguration,
.wTotalLength = static_cast<uint16_t>(
sizeof(UsbDevice::ConfigurationDescriptor) +
sizeof(UsbDevice::InterfaceDescriptor) +
sizeof(UsbDevice::EndPointDescriptor) * endpoint_descriptors.size() +
sizeof(UsbHidDevice::HidDescriptor)),
.bNumInterfaces = static_cast<uint8_t>(interface_descriptors.size()),
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0xc0,
.bMaxPower = 250};
std::vector<std::string> strings = {"", "", ""};
} // namespace
UsbHidKeyboard::UsbHidKeyboard(uint16_t vendor_id,
uint16_t product_id,
uint16_t version,
std::string vendor_name,
std::string product_name,
std::string version_name)
: UsbHidDevice(device_descriptor,
configuration_descriptor,
interface_descriptors,
endpoint_descriptors,
hid_descriptor,
report_descriptor,
strings) {
device_descriptor.idVendor = vendor_id;
device_descriptor.idProduct = product_id;
device_descriptor.bcdDevice = version;
strings[kManufacturerStringIndex - 1] = vendor_name;
strings[kProductStringIndex - 1] = product_name;
strings[kSerialNumberStringIndex - 1] = version_name;
report_.resize(8);
}
void UsbHidKeyboard::SetAutoKeyRelease(bool enabled) {
auto_key_release_ = enabled;
}
void UsbHidKeyboard::SetModifiers(uint8_t modifiers) {
modifiers_ = modifiers;
}
void UsbHidKeyboard::PressByUsageId(uint8_t usage_id) {
keycodes_.insert(usage_id);
if (reporting_) {
dirty_ = true;
} else {
Report();
}
}
void UsbHidKeyboard::ReleaseByUsageId(uint8_t usage_id) {
auto it = keycodes_.find(usage_id);
if (it != keycodes_.end()) {
keycodes_.erase(it);
if (reporting_) {
dirty_ = true;
} else {
Report();
}
}
}
void UsbHidKeyboard::OnCompleteToSend(uint8_t endpoint) {
reporting_ = false;
if (dirty_) {
Report();
}
}
void UsbHidKeyboard::Report() {
report_[0] = modifiers_;
report_[1] = 0x00;
if (keycodes_.size() > 6) {
// Phantom state
for (size_t i = 2; i < report_.size(); ++i) {
report_[i] = 0x01;
}
} else {
size_t i = 2;
for (size_t keycode : keycodes_) {
report_[i++] = keycode;
}
while (i < 8) {
report_[i++] = 0x00;
}
}
dirty_ = false;
if (auto_key_release_ && !keycodes_.empty()) {
keycodes_.clear();
modifiers_ = 0;
dirty_ = true;
}
reporting_ = true;
UsbHidDevice::Report(1, report_);
}

View File

@@ -0,0 +1,49 @@
// Copyright 2025 Google Inc.
// Use of this source code is governed by an Apache License that can be found
// in the LICENSE file.
#ifndef COMMON_USB_HID_KEYBOARD_H_
#define COMMON_USB_HID_KEYBOARD_H_
#include <cstdint>
#include <set>
#include <string>
#include <vector>
#include "usb_hid_device.h"
class UsbHidKeyboard : public UsbHidDevice {
public:
UsbHidKeyboard(uint16_t vendor_id,
uint16_t product_id,
uint16_t version,
std::string vendor_name,
std::string product_name,
std::string version_name);
UsbHidKeyboard(const UsbHidKeyboard&) = delete;
UsbHidKeyboard& operator=(const UsbHidKeyboard&) = delete;
~UsbHidKeyboard() override = default;
void SetAutoKeyRelease(bool enabled);
void SetModifiers(uint8_t modifiers);
uint8_t GetModifiers() const { return modifiers_; }
void PressByUsageId(uint8_t usage_id);
void ReleaseByUsageId(uint8_t usage_id);
private:
// Implement UsbDevice.
void OnCompleteToSend(uint8_t endpoint) override;
void Report();
bool auto_key_release_ = false;
bool reporting_ = false;
bool dirty_ = false;
uint8_t modifiers_ = 0;
std::set<uint8_t> keycodes_;
std::vector<uint8_t> report_;
};
#endif // COMMON_USB_HID_KEYBOARD_H_