blob: 5d13a397e372f589564426b201405baf6f1a29b0 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
#![no_std]
#![no_main]
#![allow(dead_code)]
use earlgrey_util::device_id::format_device_id;
use lc_ctrl::LcCtrl;
use pw_status::{Error, Result, StatusCode};
use test_usb_codegen::{handle, signals};
use userspace::time::Instant;
use userspace::{entry, syscall};
use zerocopy::IntoBytes;
use aligned::{Aligned, A4};
use hal_usb::{ConfigDescriptor, DeviceDescriptor, StringDescriptorRef};
use hal_usb::driver::UsbDriver;
use usb_driver::UsbConfig;
use usb_stack::{DescriptorSource, UsbAction, UsbClass};
use protocol_usb_cdc_acm::{CdcAcm, CdcAcmBuilder};
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_CDC_COMM_HANDLE: hal_usb::StringHandle = hal_usb::StringHandle(4);
const USB_CDC_DATA_HANDLE: hal_usb::StringHandle = hal_usb::StringHandle(5);
const CDC_BUILDER: CdcAcmBuilder = CdcAcmBuilder::new(
0, // comm_if: Communication Interface index
1, // data_if: Data Interface index
1, // comm_ep: Communication IN endpoint (Interrupt)
2, // data_out_ep: Data OUT endpoint (Bulk)
3, // data_in_ep: Data IN endpoint (Bulk)
);
const DEVICE_DESC: DeviceDescriptor = 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: ConfigDescriptor = ConfigDescriptor {
configuration_value: 1,
max_power: 250,
self_powered: false,
remote_wakeup: false,
interfaces: &[
CDC_BUILDER.comm_interface(
USB_CDC_COMM_HANDLE,
&CDC_BUILDER.comm_func_descs(),
&CDC_BUILDER.comm_endpoints(),
),
CDC_BUILDER.data_interface(USB_CDC_DATA_HANDLE, &CDC_BUILDER.data_endpoints()),
],
};
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_COMM: hal_usb::StringDescriptorRef =
hal_usb::string_descriptor!("CDC Comm Interface").as_ref();
const USB_DATA: hal_usb::StringDescriptorRef =
hal_usb::string_descriptor!("CDC Data 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() }>());
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_CDC_COMM_HANDLE => Some(USB_COMM),
USB_CDC_DATA_HANDLE => Some(USB_DATA),
_ => None,
}
}
}
fn handle_usb() -> Result<()> {
let lc_ctrl = unsafe { LcCtrl::new() };
let device_id: [u32; 8] = lc_ctrl.regs().device_id().read().into();
let mut dev_id_buf = [0_u8; 64];
let serial_str = format_device_id(&device_id, &mut dev_id_buf).map_err(|_| Error::Internal)?;
pw_log::info!("Serial Number: {}", serial_str);
let device_id_bytes = device_id.as_bytes();
let mut serial_num_buffer = Aligned::<A4, _>([0_u8; 130]);
let descriptors = MyDescriptors {
serial_desc_bytes: hal_usb::hex_utf16_descriptor_aligned(
&mut serial_num_buffer,
device_id_bytes,
)
.unwrap(),
product_desc_bytes: PRODUCT_ID_DEFAULT,
};
const USB_CONFIG: UsbConfig = UsbConfig::new(&CDC_BUILDER.eps().0, &CDC_BUILDER.eps().1);
let mut usb = usb_driver::Usb::new(unsafe { usbdev::Usbdev::new() }, USB_CONFIG);
let mut ep0 = usb_stack::SimpleEp0::new();
let mut cdc_acm = CdcAcm::<256, 256>::new(CDC_BUILDER);
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_RX_CRC_ERR
| signals::USBDEV_RX_PID_ERR
| signals::USBDEV_RX_BITSTUFF_ERR
| signals::USBDEV_FRAME
| 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() {
let mut action = match cdc_acm.handle_event(event) {
Ok(a) => a,
Err(e) => ep0.handle_event(e, &descriptors).unwrap_or(UsbAction::None),
};
action.run(&mut usb);
}
let _ = syscall::interrupt_ack(handle::USBDEV_INTERRUPTS, wait_return.pending_signals);
// Loopback received data to send buffer
while let Some(byte) = cdc_acm.rx_queue.pop() {
pw_log::info!("CDC-ACM echo byte {:#04x}", byte as u8);
let _ = cdc_acm.tx_queue.push(byte);
}
// Initiate any pending transmissions
cdc_acm.poll_transmit(&mut usb);
}
}
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!();