| // 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!(); |