forked from M-Labs/artiq
1
0
Fork 0

add kernel access to non-realtime SPI buses (#740)

This commit is contained in:
Sebastien Bourdeauducq 2017-06-18 12:45:07 +08:00
parent 424b2bfbd8
commit 8399f8893d
12 changed files with 438 additions and 177 deletions

View File

@ -30,6 +30,9 @@ def i2c_read(busno: TInt32, ack: TBool) -> TInt32:
class PCA9548:
"""Driver for the PCA9548 I2C bus switch.
I2C transactions not real-time, and are performed by the CPU without
involving RTIO.
On the KC705, this chip is used for selecting the I2C buses on the two FMC
connectors. HPC=1, LPC=2.
"""
@ -72,6 +75,9 @@ class PCA9548:
class TCA6424A:
"""Driver for the TCA6424A I2C I/O expander.
I2C transactions not real-time, and are performed by the CPU without
involving RTIO.
On the NIST QC2 hardware, this chip is used for switching the directions
of TTL buffers."""
def __init__(self, dmgr, busno=0, address=0x44, core_device="core"):

View File

@ -1,10 +1,20 @@
import numpy
from artiq.language.core import kernel, portable, now_mu, delay_mu
from artiq.language.core import syscall, kernel, portable, now_mu, delay_mu
from artiq.language.types import TInt32, TNone
from artiq.language.units import MHz
from artiq.coredevice.rtio import rtio_output, rtio_input_data
__all__ = [
"SPI_DATA_ADDR", "SPI_XFER_ADDR", "SPI_CONFIG_ADDR",
"SPI_OFFLINE", "SPI_ACTIVE", "SPI_PENDING",
"SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE",
"SPI_LSB_FIRST", "SPI_HALF_DUPLEX",
"SPIMaster", "NRTSPIMaster"
]
SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3)
(
SPI_OFFLINE,
@ -262,3 +272,65 @@ class SPIMaster:
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ,
0)
return rtio_input_data(self.channel)
@syscall(flags={"nounwind", "nowrite"})
def spi_set_config(busno: TInt32, flags: TInt32, write_div: TInt32, read_div: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def spi_set_xfer(busno: TInt32, chip_select: TInt32, write_length: TInt32, read_length: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def spi_write(busno: TInt32, data: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def spi_read(busno: TInt32) -> TInt32:
raise NotImplementedError("syscall not simulated")
class NRTSPIMaster:
"""Core device non-realtime Serial Peripheral Interface (SPI) bus master.
Owns one non-realtime SPI bus.
With this driver, SPI transactions and are performed by the CPU without
involving RTIO.
Realtime and non-realtime buses are separate and defined at bitstream
compilation time.
See :class:`SPIMaster` for a description of the methods.
"""
def __init__(self, dmgr, busno, core_device="core"):
self.core = dmgr.get(core_device)
self.busno = busno
@kernel
def set_config_mu(self, flags=0, write_div=6, read_div=6):
"""Set the ``config`` register.
Note that the non-realtime SPI cores are usually clocked by the system
clock and not the RTIO clock. In many cases, the SPI configuration is
already set by the firmware and you do not need to call this method.
The offline bit cannot be set using this method.
The SPI bus is briefly taken offline when this method is called.
"""
spi_set_config(self.busno, flags, write_div, read_div)
@kernel
def set_xfer(self, chip_select=0, write_length=0, read_length=0):
spi_set_xfer(self.busno, chip_select, write_length, read_length)
@kernel
def write(self, data=0):
spi_write(self.busno, data)
@kernel
def read(self):
return spi_read(self.busno)

View File

@ -117,8 +117,13 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(drtio_get_packet_counts = ::rtio::drtio_dbg::get_packet_counts),
api!(drtio_get_fifo_space_req_count = ::rtio::drtio_dbg::get_fifo_space_req_count),
api!(i2c_start = ::i2c_start),
api!(i2c_stop = ::i2c_stop),
api!(i2c_write = ::i2c_write),
api!(i2c_read = ::i2c_read),
api!(i2c_start = ::nrt_bus::i2c::start),
api!(i2c_stop = ::nrt_bus::i2c::stop),
api!(i2c_write = ::nrt_bus::i2c::write),
api!(i2c_read = ::nrt_bus::i2c::read),
api!(spi_set_config = ::nrt_bus::spi::set_config),
api!(spi_set_xfer = ::nrt_bus::spi::set_xfer),
api!(spi_write = ::nrt_bus::spi::write),
api!(spi_read = ::nrt_bus::spi::read),
];

View File

@ -89,6 +89,7 @@ macro_rules! raise {
pub mod eh;
mod api;
mod rtio;
mod nrt_bus;
static mut NOW: u64 = 0;
static mut LIBRARY: Option<Library<'static>> = None;
@ -220,24 +221,6 @@ extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
})
}
extern fn i2c_start(busno: i32) {
send(&I2cStartRequest { busno: busno as u8 });
}
extern fn i2c_stop(busno: i32) {
send(&I2cStopRequest { busno: busno as u8 });
}
extern fn i2c_write(busno: i32, data: i32) -> bool {
send(&I2cWriteRequest { busno: busno as u8, data: data as u8 });
recv!(&I2cWriteReply { ack } => ack)
}
extern fn i2c_read(busno: i32, ack: bool) -> i32 {
send(&I2cReadRequest { busno: busno as u8, ack: ack });
recv!(&I2cReadReply { data } => data) as i32
}
const DMA_BUFFER_SIZE: usize = 64 * 1024;
struct DmaRecorder {

View File

@ -0,0 +1,48 @@
pub mod i2c {
use ::send;
use ::recv;
use kernel_proto::*;
pub extern fn start(busno: i32) {
send(&I2cStartRequest { busno: busno as u8 });
}
pub extern fn stop(busno: i32) {
send(&I2cStopRequest { busno: busno as u8 });
}
pub extern fn write(busno: i32, data: i32) -> bool {
send(&I2cWriteRequest { busno: busno as u8, data: data as u8 });
recv!(&I2cWriteReply { ack } => ack)
}
pub extern fn read(busno: i32, ack: bool) -> i32 {
send(&I2cReadRequest { busno: busno as u8, ack: ack });
recv!(&I2cReadReply { data } => data) as i32
}
}
pub mod spi {
use ::send;
use ::recv;
use kernel_proto::*;
pub extern fn set_config(busno: i32, flags: i32, write_div: i32, read_div: i32) {
send(&SpiSetConfigRequest { busno: busno as u32, flags: flags as u8,
write_div: write_div as u8, read_div: read_div as u8 });
}
pub extern fn set_xfer(busno: i32, chip_select: i32, write_length: i32, read_length: i32) {
send(&SpiSetXferRequest { busno: busno as u32, chip_select: chip_select as u16,
write_length: write_length as u8, read_length: read_length as u8 });
}
pub extern fn write(busno: i32, data: i32) {
send(&SpiWriteRequest { busno: busno as u32, data: data as u32 });
}
pub extern fn read(busno: i32) -> i32 {
send(&SpiReadRequest { busno: busno as u32 });
recv!(&SpiReadReply { data } => data) as i32
}
}

View File

@ -1,17 +1,21 @@
use csr;
#[cfg(has_i2c)]
mod io {
use csr;
use clock;
fn half_period() { clock::spin_us(100) }
pub fn half_period() { clock::spin_us(100) }
fn sda_bit(busno: u8) -> u8 { 1 << (2 * busno + 1) }
fn scl_bit(busno: u8) -> u8 { 1 << (2 * busno) }
fn sda_i(busno: u8) -> bool {
pub fn sda_i(busno: u8) -> bool {
unsafe {
csr::i2c::in_read() & sda_bit(busno) != 0
}
}
fn sda_oe(busno: u8, oe: bool) {
pub fn sda_oe(busno: u8, oe: bool) {
unsafe {
let reg = csr::i2c::oe_read();
let reg = if oe { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
@ -19,7 +23,7 @@ fn sda_oe(busno: u8, oe: bool) {
}
}
fn sda_o(busno: u8, o: bool) {
pub fn sda_o(busno: u8, o: bool) {
unsafe {
let reg = csr::i2c::out_read();
let reg = if o { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
@ -27,7 +31,7 @@ fn sda_o(busno: u8, o: bool) {
}
}
fn scl_oe(busno: u8, oe: bool) {
pub fn scl_oe(busno: u8, oe: bool) {
unsafe {
let reg = csr::i2c::oe_read();
let reg = if oe { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
@ -35,113 +39,133 @@ fn scl_oe(busno: u8, oe: bool) {
}
}
fn scl_o(busno: u8, o: bool) {
pub fn scl_o(busno: u8, o: bool) {
unsafe {
let reg = csr::i2c::out_read();
let reg = if o { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
csr::i2c::out_write(reg)
}
}
}
#[cfg(has_i2c)]
pub fn init() {
for busno in 0..csr::CONFIG_I2C_BUS_COUNT {
let busno = busno as u8;
// Set SCL as output, and high level
scl_o(busno, true);
scl_oe(busno, true);
io::scl_o(busno, true);
io::scl_oe(busno, true);
// Prepare a zero level on SDA so that sda_oe pulls it down
sda_o(busno, false);
io::sda_o(busno, false);
// Release SDA
sda_oe(busno, false);
io::sda_oe(busno, false);
// Check the I2C bus is ready
half_period();
half_period();
if !sda_i(busno) {
io::half_period();
io::half_period();
if !io::sda_i(busno) {
error!("SDA is stuck low on bus #{}", busno)
}
}
}
#[cfg(has_i2c)]
pub fn start(busno: u8) {
// Set SCL high then SDA low
scl_o(busno, true);
half_period();
sda_oe(busno, true);
half_period();
io::scl_o(busno, true);
io::half_period();
io::sda_oe(busno, true);
io::half_period();
}
#[cfg(has_i2c)]
pub fn restart(busno: u8) {
// Set SCL low then SDA high */
scl_o(busno, false);
half_period();
sda_oe(busno, false);
half_period();
io::scl_o(busno, false);
io::half_period();
io::sda_oe(busno, false);
io::half_period();
// Do a regular start
start(busno);
}
#[cfg(has_i2c)]
pub fn stop(busno: u8) {
// First, make sure SCL is low, so that the target releases the SDA line
scl_o(busno, false);
half_period();
io::scl_o(busno, false);
io::half_period();
// Set SCL high then SDA high
sda_oe(busno, true);
scl_o(busno, true);
half_period();
sda_oe(busno, false);
half_period();
io::sda_oe(busno, true);
io::scl_o(busno, true);
io::half_period();
io::sda_oe(busno, false);
io::half_period();
}
#[cfg(has_i2c)]
pub fn write(busno: u8, data: u8) -> bool {
// MSB first
for bit in (0..8).rev() {
// Set SCL low and set our bit on SDA
scl_o(busno, false);
sda_oe(busno, data & (1 << bit) == 0);
half_period();
io::scl_o(busno, false);
io::sda_oe(busno, data & (1 << bit) == 0);
io::half_period();
// Set SCL high ; data is shifted on the rising edge of SCL
scl_o(busno, true);
half_period();
io::scl_o(busno, true);
io::half_period();
}
// Check ack
// Set SCL low, then release SDA so that the I2C target can respond
scl_o(busno, false);
half_period();
sda_oe(busno, false);
io::scl_o(busno, false);
io::half_period();
io::sda_oe(busno, false);
// Set SCL high and check for ack
scl_o(busno, true);
half_period();
io::scl_o(busno, true);
io::half_period();
// returns true if acked (I2C target pulled SDA low)
!sda_i(busno)
!io::sda_i(busno)
}
#[cfg(has_i2c)]
pub fn read(busno: u8, ack: bool) -> u8 {
// Set SCL low first, otherwise setting SDA as input may cause a transition
// on SDA with SCL high which will be interpreted as START/STOP condition.
scl_o(busno, false);
half_period(); // make sure SCL has settled low
sda_oe(busno, false);
io::scl_o(busno, false);
io::half_period(); // make sure SCL has settled low
io::sda_oe(busno, false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
scl_o(busno, false);
half_period();
io::scl_o(busno, false);
io::half_period();
// Set SCL high and shift data
scl_o(busno, true);
half_period();
if sda_i(busno) { data |= 1 << bit }
io::scl_o(busno, true);
io::half_period();
if io::sda_i(busno) { data |= 1 << bit }
}
// Send ack
// Set SCL low and pull SDA low when acking
scl_o(busno, false);
if ack { sda_oe(busno, true) }
half_period();
io::scl_o(busno, false);
if ack { io::sda_oe(busno, true) }
io::half_period();
// then set SCL high
scl_o(busno, true);
half_period();
io::scl_o(busno, true);
io::half_period();
data
}
#[cfg(not(has_i2c))]
pub fn init() {}
#[cfg(not(has_i2c))]
pub fn start(_busno: u8) {}
#[cfg(not(has_i2c))]
pub fn restart(_busno: u8) {}
#[cfg(not(has_i2c))]
pub fn stop(_busno: u8) {}
#[cfg(not(has_i2c))]
pub fn write(_busno: u8, _data: u8) -> bool { false }
#[cfg(not(has_i2c))]
pub fn read(_busno: u8, _ack: bool) { 0xff }

View File

@ -19,8 +19,9 @@ pub mod uart_console;
#[cfg(has_spiflash)]
pub mod spiflash;
#[cfg(has_i2c)]
pub mod i2c;
pub mod spi;
#[cfg(has_si5324)]
pub mod si5324;

View File

@ -0,0 +1,54 @@
#[cfg(has_converter_spi)]
use csr;
// Later this module should support other buses than the converter SPI bus,
// and add a busno parameter to differentiate them.
#[cfg(has_converter_spi)]
pub fn set_config(flags: u8, write_div: u8, read_div: u8) {
unsafe {
csr::converter_spi::offline_write(1);
csr::converter_spi::cs_polarity_write(flags >> 3 & 1);
csr::converter_spi::clk_polarity_write(flags >> 4 & 1);
csr::converter_spi::clk_phase_write(flags >> 5 & 1);
csr::converter_spi::lsb_first_write(flags >> 6 & 1);
csr::converter_spi::half_duplex_write(flags >> 7 & 1);
csr::converter_spi::clk_div_write_write(write_div);
csr::converter_spi::clk_div_read_write(read_div);
csr::converter_spi::offline_write(0);
}
}
#[cfg(has_converter_spi)]
pub fn set_xfer(chip_select: u16, write_length: u8, read_length: u8) {
unsafe {
csr::converter_spi::cs_write(chip_select as _);
csr::converter_spi::xfer_len_write_write(write_length);
csr::converter_spi::xfer_len_read_write(read_length);
}
}
#[cfg(has_converter_spi)]
pub fn write(data: u32) {
unsafe {
csr::converter_spi::data_write_write(data);
while csr::converter_spi::pending_read() != 0 {}
while csr::converter_spi::active_read() != 0 {}
}
}
#[cfg(has_converter_spi)]
pub fn read() -> u32 {
unsafe {
csr::converter_spi::data_read_read()
}
}
#[cfg(not(has_converter_spi))]
pub fn set_config(_flags: u8, _write_div: u8, _read_div: u8) {}
#[cfg(not(has_converter_spi))]
pub fn set_xfer(_chip_select: u16, _write_length: u8, _read_length: u8) {}
#[cfg(not(has_converter_spi))]
pub fn write(_data: u32) {}
#[cfg(not(has_converter_spi))]
pub fn read() -> u32 { 0 }

View File

@ -87,6 +87,12 @@ pub enum Message<'a> {
I2cReadRequest { busno: u8, ack: bool },
I2cReadReply { data: u8 },
SpiSetConfigRequest { busno: u32, flags: u8, write_div: u8, read_div: u8 },
SpiSetXferRequest { busno: u32, chip_select: u16, write_length: u8, read_length: u8 },
SpiWriteRequest { busno: u32, data: u32 },
SpiReadRequest { busno: u32 },
SpiReadReply { data: u32 },
Log(fmt::Arguments<'a>),
LogSlice(&'a str)
}

View File

@ -0,0 +1,123 @@
use session::{kern_acknowledge, kern_send};
use rtio_mgt;
use board;
use kernel_proto as kern;
use std::io;
use sched::Io;
// TODO
mod drtio_spi {
pub fn set_config(_busno: u32, _flags: u8, _write_div: u8, _read_div: u8) {}
pub fn set_xfer(_busno: u32, _chip_select: u16, _write_length: u8, _read_length: u8) {}
pub fn write(_busno: u32, _data: u32) {}
pub fn read(_busno: u32) -> u32 { 0 }
}
mod spi {
use board;
use super::drtio_spi;
pub fn set_config(busno: u32, flags: u8, write_div: u8, read_div: u8) {
let drtio = busno >> 16;
if drtio == 0 {
board::spi::set_config(flags, write_div, read_div)
} else {
drtio_spi::set_config(busno, flags, write_div, read_div)
}
}
pub fn set_xfer(busno: u32, chip_select: u16, write_length: u8, read_length: u8) {
let drtio = busno >> 16;
if drtio == 0 {
board::spi::set_xfer(chip_select, write_length, read_length)
} else {
drtio_spi::set_xfer(busno, chip_select, write_length, read_length)
}
}
pub fn write(busno: u32, data: u32) {
let drtio = busno >> 16;
if drtio == 0 {
board::spi::write(data)
} else {
drtio_spi::write(busno, data)
}
}
pub fn read(busno: u32) -> u32 {
let drtio = busno >> 16;
if drtio == 0 {
board::spi::read()
} else {
drtio_spi::read(busno)
}
}
}
pub fn process_kern_hwreq(io: &Io, request: &kern::Message) -> io::Result<bool> {
match request {
&kern::RtioInitRequest => {
info!("resetting RTIO");
rtio_mgt::init_core();
kern_acknowledge()
}
&kern::DrtioChannelStateRequest { channel } => {
let (fifo_space, last_timestamp) = rtio_mgt::drtio_dbg::get_channel_state(channel);
kern_send(io, &kern::DrtioChannelStateReply { fifo_space: fifo_space,
last_timestamp: last_timestamp })
}
&kern::DrtioResetChannelStateRequest { channel } => {
rtio_mgt::drtio_dbg::reset_channel_state(channel);
kern_acknowledge()
}
&kern::DrtioGetFifoSpaceRequest { channel } => {
rtio_mgt::drtio_dbg::get_fifo_space(channel);
kern_acknowledge()
}
&kern::DrtioPacketCountRequest => {
let (tx_cnt, rx_cnt) = rtio_mgt::drtio_dbg::get_packet_counts();
kern_send(io, &kern::DrtioPacketCountReply { tx_cnt: tx_cnt, rx_cnt: rx_cnt })
}
&kern::DrtioFifoSpaceReqCountRequest => {
let cnt = rtio_mgt::drtio_dbg::get_fifo_space_req_count();
kern_send(io, &kern::DrtioFifoSpaceReqCountReply { cnt: cnt })
}
&kern::I2cStartRequest { busno } => {
board::i2c::start(busno);
kern_acknowledge()
}
&kern::I2cStopRequest { busno } => {
board::i2c::stop(busno);
kern_acknowledge()
}
&kern::I2cWriteRequest { busno, data } => {
let ack = board::i2c::write(busno, data);
kern_send(io, &kern::I2cWriteReply { ack: ack })
}
&kern::I2cReadRequest { busno, ack } => {
let data = board::i2c::read(busno, ack);
kern_send(io, &kern::I2cReadReply { data: data })
},
&kern::SpiSetConfigRequest { busno, flags, write_div, read_div } => {
spi::set_config(busno, flags, write_div, read_div);
kern_acknowledge()
},
&kern::SpiSetXferRequest { busno, chip_select, write_length, read_length } => {
spi::set_xfer(busno, chip_select, write_length, read_length);
kern_acknowledge()
}
&kern::SpiWriteRequest { busno, data } => {
spi::write(busno, data);
kern_acknowledge()
}
&kern::SpiReadRequest { busno } => {
let data = spi::read(busno);
kern_send(io, &kern::SpiReadReply { data: data })
},
_ => return Ok(false)
}.and(Ok(true))
}

View File

@ -47,6 +47,7 @@ mod rtio_dma;
mod mgmt;
mod kernel;
mod kern_hwreq;
mod session;
#[cfg(any(has_rtio_moninj, has_drtio))]
mod moninj;

View File

@ -15,6 +15,7 @@ use board;
use rpc_proto as rpc;
use session_proto as host;
use kernel_proto as kern;
use kern_hwreq;
macro_rules! unexpected {
($($arg:tt)*) => {
@ -125,7 +126,7 @@ fn host_write(stream: &mut Write, reply: host::Reply) -> io::Result<()> {
reply.write_to(stream)
}
fn kern_send(io: &Io, request: &kern::Message) -> io::Result<()> {
pub fn kern_send(io: &Io, request: &kern::Message) -> io::Result<()> {
match request {
&kern::LoadRequest(_) => debug!("comm->kern LoadRequest(...)"),
&kern::DmaRetrieveReply { trace, duration } => {
@ -176,7 +177,7 @@ fn kern_recv<R, F>(io: &Io, f: F) -> io::Result<R>
})
}
fn kern_acknowledge() -> io::Result<()> {
pub fn kern_acknowledge() -> io::Result<()> {
mailbox::acknowledge();
Ok(())
}
@ -364,6 +365,9 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
}
kern_recv_dotrace(request);
if kern_hwreq::process_kern_hwreq(io, request)? {
return Ok(false)
}
match request {
&kern::Log(args) => {
use std::fmt::Write;
@ -387,12 +391,6 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
kern_acknowledge()
}
&kern::RtioInitRequest => {
info!("resetting RTIO");
rtio_mgt::init_core();
kern_acknowledge()
}
&kern::DmaRecordStart(name) => {
session.congress.dma_manager.record_start(name);
kern_acknowledge()
@ -419,28 +417,6 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
})
}
&kern::DrtioChannelStateRequest { channel } => {
let (fifo_space, last_timestamp) = rtio_mgt::drtio_dbg::get_channel_state(channel);
kern_send(io, &kern::DrtioChannelStateReply { fifo_space: fifo_space,
last_timestamp: last_timestamp })
}
&kern::DrtioResetChannelStateRequest { channel } => {
rtio_mgt::drtio_dbg::reset_channel_state(channel);
kern_acknowledge()
}
&kern::DrtioGetFifoSpaceRequest { channel } => {
rtio_mgt::drtio_dbg::get_fifo_space(channel);
kern_acknowledge()
}
&kern::DrtioPacketCountRequest => {
let (tx_cnt, rx_cnt) = rtio_mgt::drtio_dbg::get_packet_counts();
kern_send(io, &kern::DrtioPacketCountReply { tx_cnt: tx_cnt, rx_cnt: rx_cnt })
}
&kern::DrtioFifoSpaceReqCountRequest => {
let cnt = rtio_mgt::drtio_dbg::get_fifo_space_req_count();
kern_send(io, &kern::DrtioFifoSpaceReqCountReply { cnt: cnt })
}
&kern::WatchdogSetRequest { ms } => {
let id = session.watchdog_set.set_ms(ms)
.map_err(|()| io_error("out of watchdogs"))?;
@ -478,44 +454,6 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
kern_send(io, &kern::CachePutReply { succeeded: succeeded })
}
#[cfg(has_i2c)]
&kern::I2cStartRequest { busno } => {
board::i2c::start(busno);
kern_acknowledge()
}
#[cfg(has_i2c)]
&kern::I2cStopRequest { busno } => {
board::i2c::stop(busno);
kern_acknowledge()
}
#[cfg(has_i2c)]
&kern::I2cWriteRequest { busno, data } => {
let ack = board::i2c::write(busno, data);
kern_send(io, &kern::I2cWriteReply { ack: ack })
}
#[cfg(has_i2c)]
&kern::I2cReadRequest { busno, ack } => {
let data = board::i2c::read(busno, ack);
kern_send(io, &kern::I2cReadReply { data: data })
}
#[cfg(not(has_i2c))]
&kern::I2cStartRequest { .. } => {
kern_acknowledge()
}
#[cfg(not(has_i2c))]
&kern::I2cStopRequest { .. } => {
kern_acknowledge()
}
#[cfg(not(has_i2c))]
&kern::I2cWriteRequest { .. } => {
kern_send(io, &kern::I2cWriteReply { ack: false })
}
#[cfg(not(has_i2c))]
&kern::I2cReadRequest { .. } => {
kern_send(io, &kern::I2cReadReply { data: 0xff })
}
&kern::RunFinished => {
unsafe { kernel::stop() }
session.kernel_state = KernelState::Absent;