Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/hardware/hardware/nxp/nxp_mcu_loader/src/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 7 kB image not shown  

Quelle  spi.rs

  Sprache: Rust
 

use crate::protocol::START_BYTE;
use crate::transport::Transport;
use spidev::{SpiModeFlags, Spidev, SpidevOptions};
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::thread;
use std::time::{Duration, Instant};

const GPIO_CONSUMER_LABEL: &str = "nxp_mcu_loader";

pub const DEFAULT_SPEED_HZ: u32 = 1_000_000// 1 MHz

// RT500RM 18.7.2.9.1 SPI In-System Programming - Introduction
// "The application processor (namely, host) can use the feature nIRQ pin to improve the
// communication performance between it and the boot ROM, which can be enabled by the SetProperty
// <nIRQ pin tag> <nIRQ pin> command.
//
// Once being enabled, the host waits until it catches a negative edge on the nIRQ pin before
// reading any data from the boot ROM, and it also needs to suspend until the nIRQ pin is HIGH
// before sending any commands/data to the boot ROM."
//
// Note that this is documented on the SPI interface, but it also applies to I2C.

pub struct SpiTransport {
    spi: Spidev,
    irq_line: Option<gpio_cdev::LineHandle>,
}

impl Transport for SpiTransport {
    fn read(&mut self, buf: &mut [u8], deadline: Instant) -> io::Result<()> {
        self.wait_for_read(deadline)?;

        // NOTE: Given that self.spi is a SPI initiator, it doesn't really make sense to consider
        // deadline here, since we will perform the transfer immediately.
        let n = self.spi.read(buf)?;
        if n != buf.len() {
            // This should never happen.
            return Err(io::Error::other(format!(
                "spidev short read: {} != {}",
                n,
                buf.len()
            )));
        }
        Ok(())
    }

    fn read_start(&mut self, buf: &mut [u8], deadline: Instant) -> io::Result<()> {
        assert_eq!(buf.len(), 2);

        // From 18.7.2.6.8 Host read ACK(5a,a1) packet timing restriction:
        // """
        // If not use the IRQ pin for the In-System Programming protocol, the host
        // needs to follow the below data fetching timing for ACK from the device.
        //
        // 1. After sending out the command, the host needs to wait for at least 100us
        //    before polling the start byte 0x5A of the ACK packet sent out by the
        //    device; If fetching data is not 0x5A, delay 100us, and do polling to
        //    reread 0x5A.
        // 2. For the ACK packet 0x5A, 0xA1, after the host receives the 0x5A, a 50us
        //    delay must add before fetching the 0xA1 from the device.
        // """
        //
        // The guidance above applies to ReadAck(), but we choose to apply it here to
        // any packet which starts with a sync byte.

        // First read the start byte.
        let start_buf = buf.get_mut(0..1).unwrap(); // length checked on entry
        loop {
            if Instant::now() > deadline {
                return Err(io::Error::new(
                    io::ErrorKind::TimedOut,
                    "Timed out waiting for start byte",
                ));
            };

            if self.is_polling() {
                // Delay 100us before polling start byte (1.)
                thread::sleep(Duration::from_micros(100));
            }

            self.read(start_buf, deadline)?;

            match start_buf[0] {
                START_BYTE => break,
                0 => (),
                0xFF => (),
                val => {
                    log::warn!("Unexpected start byte: 0x{:02X}", val);
                }
            }
        }

        // Read the packet type byte
        if self.is_polling() {
            // Delay 50us before reading packet type byte (2.)
            thread::sleep(Duration::from_micros(50));
        }

        let packet_type_buf = buf.get_mut(1..2).unwrap(); // length checked on entry
        self.read(packet_type_buf, deadline)?;

        Ok(())
    }

    fn write(&mut self, buf: &[u8], deadline: Instant) -> io::Result<()> {
        self.wait_for_write(deadline)?;

        // NOTE: Given that self.spi is a SPI initiator, it doesn't really make sense to consider
        // deadline here, since we will perform the transfer immediately.
        let n = self.spi.write(buf)?;
        if n != buf.len() {
            // This should never happen.
            return Err(io::Error::other(format!(
                "spidev short write: {} != {}",
                n,
                buf.len()
            )));
        }
        Ok(())
    }

    fn set_irq_line(&mut self, line: &gpio_cdev::Line) -> io::Result<()> {
        // TODO: Change to line.events(..., EventRequestFlags::BOTH_EDGES, ...) and handle events,
        // taking stale events into consideration.
        let irq_line = line
            .request(gpio_cdev::LineRequestFlags::INPUT, 0, GPIO_CONSUMER_LABEL)
            .map_err(|err| {
                io::Error::new(
                    io::ErrorKind::Other,
                    format!("Error opening GPIO line event handle: {}", err),
                )
            })?;
        self.irq_line = Some(irq_line);
        Ok(())
    }
}

impl SpiTransport {
    pub fn new(path: impl AsRef<Path>, max_speed_hz: Option<u32>) -> io::Result<Self> {
        let max_speed_hz = max_speed_hz.unwrap_or(DEFAULT_SPEED_HZ);

        log::info!("Opening {} @ {} Hz", path.as_ref().display(), max_speed_hz);
        let mut spi = Spidev::open(path)?;

        //  RT500RM 18.7.2.9.1 Introduction
        // "needs to be configured to Mode 3 and 8 data bits mode"
        let options = SpidevOptions::new()
            .bits_per_word(8)
            .max_speed_hz(max_speed_hz)
            .mode(SpiModeFlags::SPI_MODE_3)
            .build();
        spi.configure(&options)?;

        Ok(Self {
            spi,
            irq_line: None,
        })
    }

    fn is_polling(&self) -> bool {
        // If we are not using the IRQn feature, then we are using polling mode.
        self.irq_line.is_none()
    }

    fn wait_for_read(&mut self, deadline: Instant) -> io::Result<()> {
        // "the host waits until it catches a negative edge on the nIRQ pin before reading any data
        // from the boot ROM,"
        // TODO(b/409399041): Polling the value is not safe for read: we _must_ look for a falling
        // edge.
        self.wait_for_irq_line(false, deadline)
    }

    fn wait_for_write(&mut self, deadline: Instant) -> io::Result<()> {
        // "...and it also needs to suspend until the nIRQ pin is HIGH before sending any
        // commands/data to the boot ROM."
        self.wait_for_irq_line(true, deadline)
    }

    /// Waits for the irq_line to transition to the given level (false=low; true=high).
    ///
    /// Does nothing and returns immediately if the irq line is not configured (polling mode).
    fn wait_for_irq_line(&mut self, desired_level: bool, deadline: Instant) -> io::Result<()> {
        if let Some(irq_line) = &self.irq_line {
            loop {
                if Instant::now() > deadline {
                    return Err(io::Error::new(
                        io::ErrorKind::TimedOut,
                        "Timed out waiting for IRQ line",
                    ));
                }
                // TODO(b/409399041): Polling the value is not safe for read: we _must_ look for a
                // falling edge.
                let current_level: bool = irq_line
                    .get_value()
                    .map_err(|_| io::Error::other("Failed to get irq line state"))?
                    != 0;
                if current_level == desired_level {
                    break;
                }
            }
        }
        Ok(())
    }
}

Messung V0.5 in Prozent
C=79 H=94 G=86

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-06-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.