kernel cxp: add CXP satellite support

api: compile cxp.rs with DRTIO using cfg gating
cxp: refactor xml helper fns to accept read byte closure
cxp: support non-local destination CXP syscall
cxp: pass satellite read, write, roi viewer request to core0
cxp: raise error when local has no cxp_grabber and no drtio
cxp: pass CXP error from satellite as CXPError
This commit is contained in:
morgan 2025-07-02 15:32:08 +08:00
parent 871de86e18
commit 54d494e6ec
2 changed files with 252 additions and 75 deletions

View File

@ -4,7 +4,7 @@ use core::{ffi::VaList, ptr, str};
use libc::{c_char, c_int, size_t};
use log::{info, warn};
#[cfg(has_cxp_grabber)]
#[cfg(any(has_drtio, has_cxp_grabber))]
use super::cxp;
#[cfg(has_drtio)]
use super::subkernel;
@ -129,15 +129,15 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
api!(subkernel_await_message = subkernel::await_message),
// cxp grabber
#[cfg(has_cxp_grabber)]
#[cfg(any(has_drtio, has_cxp_grabber))]
api!(cxp_download_xml_file = cxp::download_xml_file),
#[cfg(has_cxp_grabber)]
#[cfg(any(has_drtio, has_cxp_grabber))]
api!(cxp_read32 = cxp::read32),
#[cfg(has_cxp_grabber)]
#[cfg(any(has_drtio, has_cxp_grabber))]
api!(cxp_write32 = cxp::write32),
#[cfg(has_cxp_grabber)]
#[cfg(any(has_drtio, has_cxp_grabber))]
api!(cxp_start_roi_viewer = cxp::start_roi_viewer),
#[cfg(has_cxp_grabber)]
#[cfg(any(has_drtio, has_cxp_grabber))]
api!(cxp_download_roi_viewer_frame = cxp::download_roi_viewer_frame),
// Double-precision floating-point arithmetic helper functions

View File

@ -4,12 +4,18 @@ use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use cslice::CMutSlice;
use libboard_artiq::{cxp_ctrl::{DATA_MAXSIZE, Error as CtrlErr},
use libboard_artiq::drtioaux_proto::CXP_PAYLOAD_MAX_SIZE;
#[cfg(has_cxp_grabber)]
use libboard_artiq::{cxp_ctrl::DATA_MAXSIZE,
cxp_grabber::{camera_connected, roi_viewer_setup, with_tag},
cxp_packet::{read_bytes, read_u32, write_u32}};
use log::info;
use crate::{artiq_raise, pl::csr::cxp_grabber};
#[cfg(has_drtio)]
use super::{KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message};
use crate::artiq_raise;
#[cfg(has_cxp_grabber)]
use crate::pl::csr::cxp_grabber;
const ROI_MAX_SIZE: usize = 4096;
@ -24,13 +30,6 @@ enum Error {
BufferSizeTooSmall(usize, usize),
ROISizeTooBig(usize, usize),
InvalidLocalUrl(String),
CtrlPacketError(CtrlErr),
}
impl From<CtrlErr> for Error {
fn from(value: CtrlErr) -> Error {
Error::CtrlPacketError(value)
}
}
impl fmt::Display for Error {
@ -58,20 +57,22 @@ impl fmt::Display for Error {
&Error::InvalidLocalUrl(ref s) => {
write!(f, "InvalidLocalUrl - Cannot download xml file locally from {}", s)
}
&Error::CtrlPacketError(ref err) => write!(f, "{}", err),
}
}
}
fn read_xml_url(with_tag: bool) -> Result<String, Error> {
let mut addr = read_u32(0x0018, with_tag)?;
fn read_xml_url<F>(read_bytes_f: F) -> Result<String, Error>
where F: Fn(u32, &mut [u8]) {
let mut bytes: [u8; 4] = [0; 4];
read_bytes_f(0x0018, &mut bytes);
let mut addr = NetworkEndian::read_u32(&bytes);
let mut buffer = Vec::new();
// Strings stored in the bootstrap and manufacturer-specific registers space shall be NULL-terminated, encoded ASCII - Section 12.3.1 (CXP-001-2021)
// String length is not known during runtime, grabber must read 4 bytes at a time until NULL-terminated
loop {
let mut bytes: [u8; 4] = [0; 4];
read_bytes(addr, &mut bytes, with_tag)?;
read_bytes_f(addr, &mut bytes);
addr += 4;
for b in bytes {
@ -86,9 +87,7 @@ fn read_xml_url(with_tag: bool) -> Result<String, Error> {
}
}
fn read_xml_location(with_tag: bool) -> Result<(String, u32, u32), Error> {
let url = read_xml_url(with_tag)?;
fn read_xml_location(url: String) -> Result<(String, u32, u32), Error> {
// url example - Section 13.2.3 (CXP-001-2021)
// Available on camera - "Local:MyFilename.zip;B8000;33A?SchemaVersion=1.0.0"
// => ZIP file starting at address 0xB8000 in the Device with a length of 0x33A bytes
@ -107,22 +106,24 @@ fn read_xml_location(with_tag: bool) -> Result<(String, u32, u32), Error> {
Err(Error::InvalidLocalUrl(url.to_string()))
}
fn read_xml_file(buffer: &mut [i32], with_tag: bool) -> Result<u32, Error> {
let (file_name, base_addr, size) = read_xml_location(with_tag)?;
fn read_xml_file<F>(buffer: &mut [i32], read_bytes_f: F, max_read_length: usize) -> Result<u32, Error>
where F: Fn(u32, &mut [u8]) {
let url = read_xml_url(&read_bytes_f)?;
let (file_name, base_addr, size) = read_xml_location(url)?;
if buffer.len() * 4 < size as usize {
return Err(Error::BufferSizeTooSmall(size as usize, buffer.len() * 4));
return Err(Error::BufferSizeTooSmall(size as usize, buffer.len() * 4).into());
};
info!("downloading xml file {} with {} bytes...", file_name, size);
let mut v: Vec<u8> = Vec::new();
let mut addr = base_addr;
let mut bytesleft = size;
let mut bytes: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
let mut bytes: [u8; CXP_PAYLOAD_MAX_SIZE] = [0; CXP_PAYLOAD_MAX_SIZE];
while bytesleft > 0 {
let read_len = DATA_MAXSIZE.min(bytesleft as usize);
read_bytes(addr, &mut bytes[..read_len], with_tag)?;
let read_len = max_read_length.min(bytesleft as usize);
read_bytes_f(addr, &mut bytes[..read_len]);
v.extend(&bytes[..read_len]);
addr += read_len as u32;
bytesleft -= read_len as u32;
@ -139,49 +140,184 @@ fn read_xml_file(buffer: &mut [i32], with_tag: bool) -> Result<u32, Error> {
Ok((size + padding) / 4)
}
pub extern "C" fn download_xml_file(buffer: &mut CMutSlice<i32>) -> i32 {
if camera_connected() {
match read_xml_file(buffer.as_mut_slice(), with_tag()) {
Ok(size_read) => size_read as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
#[cfg(has_drtio)]
fn kernel_channel_transact(content: Message) -> Message {
unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(content);
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
}
}
#[cfg(has_drtio)]
fn drtio_read_bytes(dest: u8, addr: u32, bytes: &mut [u8]) {
let length = bytes.len() as u16;
if length as usize > CXP_PAYLOAD_MAX_SIZE {
panic!("CXPReadRequest length is too long")
}
match kernel_channel_transact(Message::CXPReadRequest {
destination: dest,
address: addr,
length,
}) {
Message::CXPReadReply { length, data } => {
bytes.copy_from_slice(&data[..length as usize]);
}
Message::CXPError(err_msg) => artiq_raise!("CXPError", err_msg),
_ => unreachable!(),
};
}
pub extern "C" fn download_xml_file(dest: i32, buffer: &mut CMutSlice<i32>) -> i32 {
match dest {
0 => {
#[cfg(has_cxp_grabber)]
{
if !camera_connected() {
artiq_raise!("CXPError", "Camera is not connected");
};
match read_xml_file(
buffer.as_mut_slice(),
|addr, bytes| {
if let Err(e) = read_bytes(addr, bytes, with_tag()) {
artiq_raise!("CXPError", format!("{}", e));
};
},
DATA_MAXSIZE,
) {
Ok(size_read) => size_read as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
}
#[cfg(not(has_cxp_grabber))]
artiq_raise!("CXPError", "CXP Grabber is not available on destination 0");
}
_ => {
#[cfg(has_drtio)]
{
match read_xml_file(
buffer.as_mut_slice(),
|addr, bytes| drtio_read_bytes(dest as u8, addr, bytes),
CXP_PAYLOAD_MAX_SIZE,
) {
Ok(size_read) => size_read as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
}
#[cfg(not(has_drtio))]
artiq_raise!("CXPError", "Destination cannot be reached");
}
} else {
artiq_raise!("CXPError", "Camera is not connected");
}
}
pub extern "C" fn read32(addr: i32) -> i32 {
if camera_connected() {
match read_u32(addr as u32, with_tag()) {
Ok(result) => result as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
pub extern "C" fn read32(dest: i32, addr: i32) -> i32 {
match dest {
0 => {
#[cfg(has_cxp_grabber)]
{
if !camera_connected() {
artiq_raise!("CXPError", "Camera is not connected");
};
match read_u32(addr as u32, with_tag()) {
Ok(result) => result as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
}
#[cfg(not(has_cxp_grabber))]
artiq_raise!("CXPError", "CXP Grabber is not available on destination 0");
}
_ => {
#[cfg(has_drtio)]
{
let mut bytes: [u8; 4] = [0; 4];
drtio_read_bytes(dest as u8, addr as u32, &mut bytes);
NetworkEndian::read_i32(&bytes)
}
#[cfg(not(has_drtio))]
artiq_raise!(
"CXPError",
format!("DRTIO is not avaiable, destination {} cannot be reached", dest)
);
}
} else {
artiq_raise!("CXPError", "Camera is not connected");
}
}
pub extern "C" fn write32(addr: i32, val: i32) {
if camera_connected() {
match write_u32(addr as u32, val as u32, with_tag()) {
Ok(_) => {}
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
pub extern "C" fn write32(dest: i32, addr: i32, val: i32) {
match dest {
0 => {
#[cfg(has_cxp_grabber)]
{
if !camera_connected() {
artiq_raise!("CXPError", "Camera is not connected");
};
match write_u32(addr as u32, val as u32, with_tag()) {
Ok(_) => {}
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
}
#[cfg(not(has_cxp_grabber))]
artiq_raise!("CXPError", "CXP Grabber is not available on destination 0");
}
_ => {
#[cfg(has_drtio)]
{
match kernel_channel_transact(Message::CXPWrite32Request {
destination: dest as u8,
address: addr as u32,
value: val as u32,
}) {
Message::CXPWrite32Reply => return,
Message::CXPError(err_msg) => artiq_raise!("CXPError", err_msg),
_ => unreachable!(),
}
}
#[cfg(not(has_drtio))]
artiq_raise!(
"CXPError",
format!("DRTIO is not avaiable, destination {} cannot be reached", dest)
);
}
} else {
artiq_raise!("CXPError", "Camera is not connected");
}
}
pub extern "C" fn start_roi_viewer(x0: i32, y0: i32, x1: i32, y1: i32) {
pub extern "C" fn start_roi_viewer(dest: i32, x0: i32, y0: i32, x1: i32, y1: i32) {
let (width, height) = ((x1 - x0) as usize, (y1 - y0) as usize);
if width * height > ROI_MAX_SIZE || height > ROI_MAX_SIZE / 4 {
artiq_raise!("CXPError", format!("{}", Error::ROISizeTooBig(width, height)));
} else {
roi_viewer_setup(x0 as u16, y0 as u16, x1 as u16, y1 as u16);
}
match dest {
0 => {
#[cfg(has_cxp_grabber)]
{
roi_viewer_setup(x0 as u16, y0 as u16, x1 as u16, y1 as u16)
}
#[cfg(not(has_cxp_grabber))]
artiq_raise!("CXPError", "CXP Grabber is not available on destination 0");
}
_ => {
#[cfg(has_drtio)]
{
match kernel_channel_transact(Message::CXPROIViewerSetupRequest {
destination: dest as u8,
x0: x0 as u16,
y0: y0 as u16,
x1: x1 as u16,
y1: y1 as u16,
}) {
Message::CXPROIViewerSetupReply => return,
_ => unreachable!(),
}
}
#[cfg(not(has_drtio))]
artiq_raise!(
"CXPError",
format!("DRTIO is not avaiable, destination {} cannot be reached", dest)
);
}
}
}
pub extern "C" fn download_roi_viewer_frame(buffer: &mut CMutSlice<i64>) -> ROIViewerFrame {
pub extern "C" fn download_roi_viewer_frame(dest: i32, buffer: &mut CMutSlice<i64>) -> ROIViewerFrame {
if buffer.len() * 4 < ROI_MAX_SIZE {
// each pixel is 16 bits
artiq_raise!(
@ -191,30 +327,71 @@ pub extern "C" fn download_roi_viewer_frame(buffer: &mut CMutSlice<i64>) -> ROIV
};
let buf = buffer.as_mut_slice();
unsafe {
while cxp_grabber::roi_viewer_ready_read() == 0 {}
cxp_grabber::roi_viewer_ready_write(1);
let (width, height, pixel_code);
match dest {
0 => {
#[cfg(has_cxp_grabber)]
unsafe {
while cxp_grabber::roi_viewer_ready_read() == 0 {}
let mut i = 0;
while cxp_grabber::roi_viewer_fifo_stb_read() == 1 {
buf[i] = cxp_grabber::roi_viewer_fifo_data_read() as i64;
i += 1;
cxp_grabber::roi_viewer_fifo_ack_write(1);
}
cxp_grabber::roi_viewer_ready_write(1);
let mut i = 0;
while cxp_grabber::roi_viewer_fifo_stb_read() == 1 {
buf[i] = cxp_grabber::roi_viewer_fifo_data_read() as i64;
i += 1;
cxp_grabber::roi_viewer_fifo_ack_write(1);
width = cxp_grabber::roi_viewer_x1_read() - cxp_grabber::roi_viewer_x0_read();
height = cxp_grabber::roi_viewer_y1_read() - cxp_grabber::roi_viewer_y0_read();
pixel_code = cxp_grabber::stream_decoder_pixel_format_code_read();
}
#[cfg(not(has_cxp_grabber))]
artiq_raise!("CXPError", "CXP Grabber is not available on destination 0");
}
let width = (cxp_grabber::roi_viewer_x1_read() - cxp_grabber::roi_viewer_x0_read()) as i32;
let height = (cxp_grabber::roi_viewer_y1_read() - cxp_grabber::roi_viewer_y0_read()) as i32;
let pixel_width = match cxp_grabber::stream_decoder_pixel_format_code_read() {
0x0101 => 8,
0x0102 => 10,
0x0103 => 12,
0x0104 => 14,
0x0105 => 16,
_ => artiq_raise!("CXPError", "UnsupportedPixelFormat"),
};
ROIViewerFrame {
width,
height,
pixel_width,
_ => {
#[cfg(has_drtio)]
{
let mut i = 0;
loop {
match kernel_channel_transact(Message::CXPROIViewerDataRequest {
destination: dest as u8,
}) {
Message::CXPROIVIewerPixelDataReply { length, data } => {
for d in &data[..length as usize] {
buf[i] = *d as i64;
i += 1;
}
}
Message::CXPROIVIewerFrameDataReply {
width: w,
height: h,
pixel_code: p,
} => {
(width, height, pixel_code) = (w, h, p);
break;
}
_ => unreachable!(),
}
}
}
#[cfg(not(has_drtio))]
artiq_raise!(
"CXPError",
format!("DRTIO is not avaiable, destination {} cannot be reached", dest)
);
}
};
let pixel_width = match pixel_code {
0x0101 => 8,
0x0102 => 10,
0x0103 => 12,
0x0104 => 14,
0x0105 => 16,
_ => artiq_raise!("CXPError", "UnsupportedPixelFormat"),
};
ROIViewerFrame {
width: width as i32,
height: height as i32,
pixel_width: pixel_width as i32,
}
}