blob: 955c7d396ede9e9adb24a7b23cd6cbf3ff2597b4 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
#![no_std]
#![no_main]
use earlgrey_util::device_id::format_device_id;
use lc_ctrl::LcCtrl;
use pw_status::{Error, Result, StatusCode};
use zerocopy::IntoBytes;
use test_usb_codegen::{handle, signals};
use userspace::time::Instant;
use userspace::{entry, syscall};
use aligned::{Aligned, A4};
use hal_usb::driver::{UsbDriver, UsbEvent, UsbPacket};
use hal_usb::{Direction, StringDescriptorRef, USB_CLASS_VENDOR};
use usb_driver::{EpIn, EpOut, UsbConfig};
use usb_stack::{
//UsbActionRun,
DescriptorSource,
UsbAction,
};
const USB_VENDOR_HANDLE: hal_usb::StringHandle = hal_usb::StringHandle(1);
const USB_PRODUCT_HANDLE: hal_usb::StringHandle = hal_usb::StringHandle(2);
const USB_SERIAL_HANDLE: hal_usb::StringHandle = hal_usb::StringHandle(3);
const USB_TEST_HANDLE: hal_usb::StringHandle = hal_usb::StringHandle(4);
static DEVICE_DESC: hal_usb::DeviceDescriptor = hal_usb::DeviceDescriptor {
device_class: hal_usb::DeviceClass::SPECIFIED_BY_INTERFACE,
device_sub_class: 0x00,
device_protocol: 0x00,
max_packet_size: 64,
vendor_id: 0x18d1, // Google, Inc.
product_id: 0x503a, // STWG USB Fullspeed IP.
device_release_num: 0x0100,
manufacturer: USB_VENDOR_HANDLE,
product: USB_PRODUCT_HANDLE,
serial_num: USB_SERIAL_HANDLE,
};
const CONFIG_DESC: hal_usb::ConfigDescriptor = hal_usb::ConfigDescriptor {
configuration_value: 1,
max_power: 250,
self_powered: false,
remote_wakeup: false,
interfaces: &[hal_usb::InterfaceDescriptor {
name: USB_TEST_HANDLE,
interface_number: 1,
alternate_setting: 0,
interface_class: USB_CLASS_VENDOR,
interface_sub_class: 0xFF,
interface_protocol: 1,
func_descs: &[],
endpoints: &[
hal_usb::EndpointDescriptor {
direction: Direction::DeviceToHost,
endpoint_num: 1,
interval: 0,
max_packet_size: 64,
transfer_type: hal_usb::TransferType::Bulk,
},
hal_usb::EndpointDescriptor {
direction: Direction::HostToDevice,
endpoint_num: 2,
interval: 0,
max_packet_size: 64,
transfer_type: hal_usb::TransferType::Bulk,
},
],
}],
};
const STRING_DESC_0: hal_usb::StringDescriptor0 = hal_usb::StringDescriptor0 {
langs: &[
// English - United States
0x0409,
],
};
const VENDOR_ID: hal_usb::StringDescriptorRef = hal_usb::string_descriptor!("Google Inc.").as_ref();
const PRODUCT_ID_DEFAULT: hal_usb::StringDescriptorRef =
hal_usb::string_descriptor!("OpenPRoT Earlgrey").as_ref();
const USB_TEST: hal_usb::StringDescriptorRef =
hal_usb::string_descriptor!("USB Test Interface").as_ref();
struct MyDescriptors<'a> {
serial_desc_bytes: StringDescriptorRef<'a>,
product_desc_bytes: StringDescriptorRef<'a>,
}
impl DescriptorSource for MyDescriptors<'_> {
const DEVICE_DESC_BYTES: &'static Aligned<A4, [u8]> = &Aligned(DEVICE_DESC.serialize());
const CONFIG_DESC_BYTES: &'static Aligned<A4, [u8]> =
&Aligned(CONFIG_DESC.serialize::<{ CONFIG_DESC.total_size() }>());
const STRING_DESC_0_BYTES: &'static Aligned<A4, [u8]> =
&Aligned(STRING_DESC_0.serialize::<{ STRING_DESC_0.total_size() }>());
// We advertise that we are self-powered and do not support remote wakeup.
const DEVICE_STATUS: Aligned<A4, [u8; 2]> = Aligned([1u8, 0]);
fn get_string(
&self,
handle: hal_usb::StringHandle,
_lang: u16,
) -> Option<hal_usb::StringDescriptorRef<'_>> {
match handle {
USB_VENDOR_HANDLE => Some(VENDOR_ID),
USB_PRODUCT_HANDLE => Some(self.product_desc_bytes),
USB_SERIAL_HANDLE => Some(self.serial_desc_bytes),
USB_TEST_HANDLE => Some(USB_TEST),
_ => None,
}
}
}
const CONTROL_EP_OUT_NUM: u8 = 0;
fn handle_usb() -> Result<()> {
let mut serial_num_buffer = Aligned::<A4, _>([0_u8; 130]);
let lc_ctrl = unsafe { LcCtrl::new() };
let device_id: [u32; 8] = lc_ctrl.regs().device_id().read().into();
let mut serial_ascii = [0u8; 64];
let serial_str =
format_device_id(&device_id, &mut serial_ascii).map_err(|_| Error::Internal)?;
pw_log::info!("Serial Number: {}", serial_str);
let device_id_bytes = device_id.as_bytes();
let descriptors = MyDescriptors {
serial_desc_bytes: hal_usb::hex_utf16_descriptor_aligned(
&mut serial_num_buffer,
device_id_bytes,
)
.map_err(|_| Error::Internal)?,
product_desc_bytes: PRODUCT_ID_DEFAULT,
};
const USB_EP_IN: EpIn = EpIn {
num: 1,
buf_pool_size: 8,
};
const USB_EP_OUT: EpOut = EpOut {
num: 2,
set_nak: true,
};
const USB_CONFIG: UsbConfig = UsbConfig::new(&[USB_EP_IN], &[USB_EP_OUT]);
let mut usb = usb_driver::Usb::new(unsafe { usbdev::Usbdev::new() }, USB_CONFIG);
let mut ep0 = usb_stack::SimpleEp0::new();
let mut ep0_action: UsbAction<'_> = UsbAction::None;
loop {
let wait_return = syscall::object_wait(
handle::USBDEV_INTERRUPTS,
signals::USBDEV_PKT_RECEIVED
| signals::USBDEV_PKT_SENT
| signals::USBDEV_DISCONNECTED
| signals::USBDEV_HOST_LOST
| signals::USBDEV_LINK_RESET
| signals::USBDEV_LINK_SUSPEND
| signals::USBDEV_LINK_RESUME
| signals::USBDEV_AV_OUT_EMPTY
| signals::USBDEV_RX_FULL
| signals::USBDEV_AV_OVERFLOW
//| signals::USBDEV_LINK_IN_ERR
| signals::USBDEV_RX_CRC_ERR
| signals::USBDEV_RX_PID_ERR
| signals::USBDEV_RX_BITSTUFF_ERR
| signals::USBDEV_FRAME
//| signals::USBDEV_POWERED
//| signals::USBDEV_LINK_OUT_ERR
| signals::USBDEV_AV_SETUP_EMPTY,
Instant::MAX,
)?;
if wait_return.user_data != 0 {
pw_log::error!("Incorrect WaitReturn values");
return Err(Error::Unknown);
}
while let Some(event) = usb.poll() {
match event {
UsbEvent::SetupPacket { pkt, endpoint } => {
if endpoint == 0 {
let dir = pkt.request().direction() as u32;
let ty = pkt.request().request_type() as u32;
let recip = pkt.request().recipient() as u32;
let bmreq = (dir << 7) | (ty << 5) | (recip);
let request = pkt.request().request();
pw_log::debug!(
"SETUP: {:02x} {:02x} val={:04x} idx={:04x} len={:04x}",
bmreq as u32,
request as u8,
pkt.value() as u16,
pkt.index() as u16,
pkt.length() as u16,
);
ep0_action = ep0
.handle_event(event, &descriptors)
.unwrap_or(UsbAction::None);
} else {
pw_log::debug!("Setup on bad EP {}", endpoint as u8);
}
}
UsbEvent::DataOutPacket(pkt) => match u8::try_from(pkt.endpoint_index()).unwrap() {
CONTROL_EP_OUT_NUM => {
pw_log::debug!("OUT on control ep");
}
ep => {
pw_log::debug!(
"Unhandled OUT on EP {} len={}",
ep as u8,
pkt.len() as usize
);
}
},
UsbEvent::UsbReset => {
pw_log::debug!("USB reset");
}
_ => {
ep0_action.merge(
ep0.handle_event(event, &descriptors)
.unwrap_or(UsbAction::None),
);
}
}
ep0_action.run(&mut usb);
}
let _ = syscall::interrupt_ack(handle::USBDEV_INTERRUPTS, wait_return.pending_signals);
}
}
fn usb_setup_pinmux() {
use top_earlgrey::{PinmuxInsel, PinmuxPeripheralIn};
let mut pinmux = unsafe { pinmux::PinmuxAon::new() };
pinmux
.regs_mut()
.mio_periph_insel()
.at(PinmuxPeripheralIn::UsbdevSense as usize)
.modify(|_| (PinmuxInsel::ConstantOne as u32).into());
}
#[entry]
fn entry() -> Result<()> {
pw_log::info!("🔄 RUNNING");
usb_setup_pinmux();
let ret = handle_usb();
if ret.is_err() {
pw_log::error!("❌ FAIL: {}", ret.status_code() as u32);
} else {
pw_log::info!("✅ PASS");
}
ret
}
util_panic::make_panic_handler!();