mirror of
https://github.com/google/mozc-devices.git
synced 2025-11-08 16:53:28 +03:00
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:
45
mozc-dial/firmware/common/dial_controller.cc
Normal file
45
mozc-dial/firmware/common/dial_controller.cc
Normal 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;
|
||||
}
|
||||
28
mozc-dial/firmware/common/dial_controller.h
Normal file
28
mozc-dial/firmware/common/dial_controller.h
Normal 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
|
||||
108
mozc-dial/firmware/common/motor_controller.cc
Normal file
108
mozc-dial/firmware/common/motor_controller.cc
Normal 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;
|
||||
}
|
||||
38
mozc-dial/firmware/common/motor_controller.h
Normal file
38
mozc-dial/firmware/common/motor_controller.h
Normal 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_
|
||||
32
mozc-dial/firmware/common/photo_sensor.cc
Normal file
32
mozc-dial/firmware/common/photo_sensor.cc
Normal 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;
|
||||
}
|
||||
33
mozc-dial/firmware/common/photo_sensor.h
Normal file
33
mozc-dial/firmware/common/photo_sensor.h
Normal 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_
|
||||
145
mozc-dial/firmware/common/usage_tables.cc
Normal file
145
mozc-dial/firmware/common/usage_tables.cc
Normal 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
|
||||
54
mozc-dial/firmware/common/usage_tables.h
Normal file
54
mozc-dial/firmware/common/usage_tables.h
Normal 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_
|
||||
353
mozc-dial/firmware/common/usb_device.cc
Normal file
353
mozc-dial/firmware/common/usb_device.cc
Normal 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;
|
||||
}
|
||||
145
mozc-dial/firmware/common/usb_device.h
Normal file
145
mozc-dial/firmware/common/usb_device.h
Normal 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_
|
||||
94
mozc-dial/firmware/common/usb_hid_device.cc
Normal file
94
mozc-dial/firmware/common/usb_hid_device.cc
Normal 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);
|
||||
}
|
||||
}
|
||||
60
mozc-dial/firmware/common/usb_hid_device.h
Normal file
60
mozc-dial/firmware/common/usb_hid_device.h
Normal 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_
|
||||
203
mozc-dial/firmware/common/usb_hid_keyboard.cc
Normal file
203
mozc-dial/firmware/common/usb_hid_keyboard.cc
Normal 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_);
|
||||
}
|
||||
49
mozc-dial/firmware/common/usb_hid_keyboard.h
Normal file
49
mozc-dial/firmware/common/usb_hid_keyboard.h
Normal 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_
|
||||
Reference in New Issue
Block a user