1use super::*;
2use std::ptr::NonNull;
3
4#[derive(Debug)]
5pub struct Twi {
6 state: NonNull<TwiState>,
7}
8
9impl Twi {
10 pub unsafe fn new(id: u8, avr: &Avr) -> Option<Self> {
18 let ioctl = IoCtl::TwiGetIrq { id };
19 let irq_input = avr.try_io_getirq(ioctl, ffi::TWI_IRQ_INPUT)?;
20 let irq_output = avr.try_io_getirq(ioctl, ffi::TWI_IRQ_OUTPUT)?;
21
22 let this = Self {
23 state: NonNull::from(Box::leak(Box::new(TwiState {
24 slave: None,
25 irq_input,
26 }))),
27 };
28
29 unsafe {
30 Avr::irq_register_notify(
31 irq_output,
32 Some(Self::on_output),
33 this.state.as_ptr(),
34 );
35 }
36
37 Some(this)
38 }
39
40 pub fn set_slave(&mut self, slave: impl TwiSlave + 'static) {
41 self.state_mut().slave = Some(Box::new(slave));
42 }
43
44 fn state_mut(&mut self) -> &mut TwiState {
45 unsafe { self.state.as_mut() }
49 }
50
51 unsafe extern "C" fn on_output(
52 _: NonNull<ffi::avr_irq_t>,
53 value: u32,
54 mut state: NonNull<TwiState>,
55 ) {
56 unsafe {
57 let state = state.as_mut();
58
59 if let Some(slave) = &mut state.slave {
60 if let Some(packet) = slave.recv(TwiPacket::decode(value)) {
61 ffi::avr_raise_irq(
62 state.irq_input.as_ptr(),
63 packet.encode(),
64 );
65 }
66 }
67 }
68 }
69}
70
71impl Drop for Twi {
72 fn drop(&mut self) {
73 unsafe {
74 drop(Box::from_raw(self.state.as_ptr()));
75 }
76 }
77}
78
79struct TwiState {
80 slave: Option<Box<dyn TwiSlave>>,
81 irq_input: NonNull<ffi::avr_irq_t>,
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub struct TwiPacket {
86 pub msg: u8,
87 pub addr: u8,
88 pub data: u8,
89}
90
91impl TwiPacket {
92 fn decode(packet: u32) -> Self {
93 let [_, msg, addr, data] = packet.to_le_bytes();
94
95 Self { msg, addr, data }
96 }
97
98 fn encode(self) -> u32 {
99 u32::from_le_bytes([0, self.msg, self.addr, self.data])
100 }
101
102 pub const MSG_START: u8 = ffi::TWI_COND_START as u8;
103 pub const MSG_STOP: u8 = ffi::TWI_COND_STOP as u8;
104 pub const MSG_ADDR: u8 = ffi::TWI_COND_ADDR as u8;
105 pub const MSG_ACK: u8 = ffi::TWI_COND_ACK as u8;
106 pub const MSG_WRITE: u8 = ffi::TWI_COND_WRITE as u8;
107 pub const MSG_READ: u8 = ffi::TWI_COND_READ as u8;
108
109 pub fn is_start(&self) -> bool {
110 self.msg & Self::MSG_START > 0
111 }
112
113 pub fn is_stop(&self) -> bool {
114 self.msg & Self::MSG_STOP > 0
115 }
116
117 pub fn is_addr(&self) -> bool {
118 self.msg & Self::MSG_ADDR > 0
119 }
120
121 pub fn is_ack(&self) -> bool {
122 self.msg & Self::MSG_ACK > 0
123 }
124
125 pub fn is_write(&self) -> bool {
126 self.msg & Self::MSG_WRITE > 0
127 }
128
129 pub fn is_read(&self) -> bool {
130 self.msg & Self::MSG_READ > 0
131 }
132
133 pub fn respond_ack(&self) -> Self {
134 Self {
135 msg: Self::MSG_ACK,
136 addr: self.addr,
137 data: 1,
138 }
139 }
140
141 pub fn respond_data(&self, data: u8) -> Self {
142 Self {
143 msg: Self::MSG_READ,
144 addr: self.addr,
145 data,
146 }
147 }
148}
149
150pub trait TwiSlave {
151 fn recv(&mut self, packet: TwiPacket) -> Option<TwiPacket>;
152}
153
154impl<T> TwiSlave for T
155where
156 T: FnMut(TwiPacket) -> Option<TwiPacket>,
157{
158 fn recv(&mut self, packet: TwiPacket) -> Option<TwiPacket> {
159 (self)(packet)
160 }
161}