use crate::{
arm_dcache_delete, mem,
phys::{addrs::USB, usb::models::*, usb::registers::*},
phys::{assign, read_word, usb::descriptors::*, usb::*},
system::{
buffer::*,
vector::{Queue, Stack},
},
};
const RX_BUFFER_SIZE: usize = 512;
const TX_BUFFER_SIZE: usize = 512;
static mut BUFFER: Buffer<512, u8> = Buffer::new(0);
#[link_section = ".descriptors"]
static mut TX_DTD: UsbEndpointTransferDescriptor = UsbEndpointTransferDescriptor::new();
#[link_section = ".descriptors"]
static mut RX_DTD: UsbEndpointTransferDescriptor = UsbEndpointTransferDescriptor::new();
static mut TX_BUFFER_TRANSIENT: Buffer<TX_BUFFER_SIZE, u8> = Buffer::new(0);
#[link_section = ".dmabuffers"]
static mut RX_BUFFER: BufferPage = BufferPage::new();
#[link_section = ".dmabuffers"]
static mut TX_BUFFER: BufferPage = BufferPage::new();
static mut CONFIGURED: bool = false;
const CDC_STATUS_INTERFACE: u8 = 0;
const CDC_DATA_INTERFACE: u8 = 1;
const CDC_ACM_SIZE: u16 = 16;
const CDC_RX_SIZE_480: u16 = 512;
const CDC_TX_SIZE_480: u16 = 512;
const CDC_RX_SIZE_12: u16 = 64;
const CDC_TX_SIZE_12: u16 = 64;
const CDC_ACM_ENDPOINT: u8 = 2;
const CDC_RX_ENDPOINT: u8 = 3;
const CDC_TX_ENDPOINT: u8 = 4;
pub fn usb_serial_init() {
setup_cdc_descriptors();
usb_attach_setup_callback(usb_serial_configure);
usb_attach_irq_handler(handle_irq);
}
fn usb_timer_oneshot() {
let timer_val = read_word(USB + 0x84) & 0xFFDFFF;
if timer_val == 0 {
assign(USB + 0x84, (1 << 31) | (1 << 30));
}
}
fn handle_irq(irq_status: u32) {
if (irq_status & TI0) > 0 {
usb_serial_flush();
}
}
fn usb_serial_configure(packet: SetupPacket) {
match packet.bm_request_and_type {
0x900 => {
usb_setup_endpoint(
CDC_ACM_ENDPOINT as usize,
Some(EndpointConfig {
endpoint_type: EndpointType::INTERRUPT,
size: CDC_ACM_SIZE,
zlt: false,
callback: None,
}),
None,
);
usb_setup_endpoint(
CDC_TX_ENDPOINT as usize,
Some(EndpointConfig {
endpoint_type: EndpointType::BULK,
size: CDC_TX_SIZE_480,
zlt: false,
callback: Some(tx_callback),
}),
None,
);
usb_setup_endpoint(
CDC_RX_ENDPOINT as usize,
None,
Some(EndpointConfig {
endpoint_type: EndpointType::BULK,
size: CDC_RX_SIZE_480,
zlt: false,
callback: Some(rx_callback),
}),
);
unsafe {
TX_DTD.clear();
RX_DTD.clear();
}
rx_queue_transfer();
assign(USB + 0x80, 0x0003E7); assign(USBINTR, read_word(USBINTR) | TI0);
assign(USB + 0x84, (1 << 31) | (1 << 30));
unsafe {
CONFIGURED = true;
}
}
_ => {
return;
}
}
}
fn rx_queue_transfer() {
let rx_buffer_len = RX_BUFFER_SIZE as u32;
let buffer_ptr = unsafe { RX_BUFFER.as_ptr() as u32 };
arm_dcache_delete(buffer_ptr, rx_buffer_len);
usb_prepare_transfer(unsafe { &mut RX_DTD }, buffer_ptr, rx_buffer_len, true);
usb_receive(CDC_RX_ENDPOINT as usize, unsafe { &mut RX_DTD });
}
fn rx_callback(packet: &UsbEndpointTransferDescriptor) {
if unsafe { CONFIGURED } == false {
return;
}
let qh = usb_get_queuehead(CDC_RX_ENDPOINT as usize, false);
let len = (RX_BUFFER_SIZE as u32) - (packet.status >> 16) & 0x7FFF;
for index in 0..len {
unsafe {
BUFFER.enqueue(RX_BUFFER.bytes[index as usize]);
}
}
if qh.first_transfer == qh.last_transfer && qh.first_transfer == 0 {
rx_queue_transfer();
}
}
pub fn usb_serial_available() -> usize {
return unsafe { BUFFER.size() };
}
pub fn usb_serial_read() -> Option<u8> {
return unsafe { BUFFER.dequeue() };
}
pub fn usb_serial_peek() -> Option<u8> {
unsafe {
if BUFFER.size() > 0 {
return Some(BUFFER.data[0]);
} else {
return None;
}
}
}
fn tx_callback(packet: &UsbEndpointTransferDescriptor) {
if (packet.status & 0x80) != 0 {
usb_timer_oneshot();
}
}
pub fn usb_serial_putchar(byte: u8) {
usb_serial_write(&[byte]);
}
pub fn usb_serial_write(bytes: &[u8]) {
unsafe {
for byte in bytes {
TX_BUFFER_TRANSIENT.push(*byte);
}
}
usb_timer_oneshot();
}
pub fn usb_serial_flush() -> u32 {
if unsafe { CONFIGURED == false } {
return 0;
}
if unsafe { TX_BUFFER_TRANSIENT.size() } > 0 {
let dtd = unsafe { &mut TX_DTD };
if (dtd.status & 0x80) > 0 {
return 0;
}
if (dtd.status & 0xFF) > 0 {
}
let len = unsafe { TX_BUFFER_TRANSIENT.size() } as u32;
let src_ptr = unsafe { TX_BUFFER_TRANSIENT.data.as_ptr() } as u32;
let dst_ptr = unsafe { TX_BUFFER.as_ptr() } as u32;
mem::copy(src_ptr, dst_ptr, len);
unsafe {
TX_BUFFER_TRANSIENT.clear();
}
usb_prepare_transfer(dtd, dst_ptr, len, true);
usb_transmit(CDC_TX_ENDPOINT as usize, dtd);
return len;
}
return 0;
}
fn setup_cdc_descriptors() {
let descriptors = usb_get_descriptors();
descriptors.with_interface(
0x200,
0x0,
&[
8, 11, CDC_STATUS_INTERFACE, 2, 0x02, 0x02, 0x01, 0,
9, 4, CDC_STATUS_INTERFACE, 0, 1, 0x02, 0x02, 0x01, 0, 5, 0x24, 0x00, 0x10,
0x01, 5, 0x24, 0x01, 0x01, 1, 4, 0x24, 0x02, 0x06, 5, 0x24, 0x06, CDC_STATUS_INTERFACE, CDC_DATA_INTERFACE, 7, 5, CDC_ACM_ENDPOINT | 0x80, 0x03, lsb(CDC_ACM_SIZE),
msb(CDC_ACM_SIZE), 5, 9, 4, CDC_DATA_INTERFACE, 0, 2, 0x0A, 0x00, 0x00, 0, 7, 5, CDC_RX_ENDPOINT, 0x02, lsb(CDC_RX_SIZE_480),
msb(CDC_RX_SIZE_480), 0, 7, 5, CDC_TX_ENDPOINT | 0x80, 0x02, lsb(CDC_TX_SIZE_480),
msb(CDC_TX_SIZE_480), 0,
],
);
descriptors.with_interface(
0x700,
0x0,
&[
8, 11, CDC_STATUS_INTERFACE, 2, 0x02, 0x02, 0x00, 0,
9, 4, CDC_STATUS_INTERFACE, 0, 1, 0x02, 0x02, 0x00, 0, 5, 0x24, 0x00, 0x10,
0x01, 5, 0x24, 0x01, 0x01, 1, 4, 0x24, 0x02, 0x06, 5, 0x24, 0x06, CDC_STATUS_INTERFACE, CDC_DATA_INTERFACE, 7, 5, CDC_ACM_ENDPOINT | 0x80, 0x03, CDC_ACM_SIZE as u8,
0, 16, 9, 4, CDC_DATA_INTERFACE, 0, 2, 0x0A, 0x00, 0x00, 0, 7, 5, CDC_RX_ENDPOINT, 0x02, lsb(CDC_RX_SIZE_12),
msb(CDC_RX_SIZE_12), 0, 7, 5, CDC_TX_ENDPOINT | 0x80, 0x02, lsb(CDC_TX_SIZE_12),
msb(CDC_TX_SIZE_12), 0,
],
);
}