| use core::cell::Cell; |
| use core::ops::{Index, IndexMut}; |
| use kernel::common::cells::{OptionalCell, TakeCell}; |
| use kernel::hil; |
| |
| pub static mut TXBUFFER: [u8; PAGE_SIZE + 5] = [0; PAGE_SIZE + 5]; |
| pub static mut RXBUFFER: [u8; PAGE_SIZE + 5] = [0; PAGE_SIZE + 5]; |
| const PAGE_SIZE: usize = 512; |
| |
| pub struct NexusSpiflashPage(pub [u8; PAGE_SIZE]); |
| |
| impl Default for NexusSpiflashPage { |
| fn default() -> Self { |
| Self { 0: [0; PAGE_SIZE] } |
| } |
| } |
| |
| impl Index<usize> for NexusSpiflashPage { |
| type Output = u8; |
| fn index(&self, idx: usize) -> &u8 { |
| &self.0[idx] |
| } |
| } |
| |
| impl IndexMut<usize> for NexusSpiflashPage { |
| fn index_mut(&mut self, idx: usize) -> &mut u8 { |
| &mut self.0[idx] |
| } |
| } |
| |
| impl AsMut<[u8]> for NexusSpiflashPage { |
| fn as_mut(&mut self) -> &mut [u8] { |
| &mut self.0 |
| } |
| } |
| |
| enum Opcodes { |
| Read = 0x03, |
| } |
| |
| #[derive(Clone, Copy, PartialEq)] |
| enum State { |
| Idle, |
| WriteOpcode, |
| ReadPage { offset: usize }, |
| } |
| |
| pub struct NexusSpiflash<'a, S: hil::spi::SpiMasterDevice + 'a> { |
| spi: &'a S, |
| state: Cell<State>, |
| txbuffer: TakeCell<'static, [u8]>, |
| rxbuffer: TakeCell<'static, [u8]>, |
| client: OptionalCell<&'a dyn hil::flash::Client<NexusSpiflash<'a, S>>>, |
| client_page: TakeCell<'static, NexusSpiflashPage>, |
| } |
| |
| impl<'a, S: hil::spi::SpiMasterDevice + 'a> NexusSpiflash<'a, S> { |
| pub fn new( |
| spi: &'a S, |
| txbuffer: &'static mut [u8], |
| rxbuffer: &'static mut [u8], |
| ) -> NexusSpiflash<'a, S> { |
| NexusSpiflash { |
| spi: spi, |
| state: Cell::new(State::Idle), |
| txbuffer: TakeCell::new(txbuffer), |
| rxbuffer: TakeCell::new(rxbuffer), |
| client: OptionalCell::empty(), |
| client_page: TakeCell::empty(), |
| } |
| } |
| } |
| |
| impl<'a, S: hil::spi::SpiMasterDevice + 'a, C: hil::flash::Client<Self>> |
| hil::flash::HasClient<'a, C> for NexusSpiflash<'a, S> |
| { |
| fn set_client(&'a self, client: &'a C) { |
| self.client.set(client); |
| } |
| } |
| |
| impl<'a, S: hil::spi::SpiMasterDevice + 'a> hil::flash::Flash for NexusSpiflash<'a, S> { |
| type Page = NexusSpiflashPage; |
| fn read_page( |
| &self, |
| page: usize, |
| read_buffer: &'static mut <Self as kernel::hil::flash::Flash>::Page, |
| ) -> core::result::Result< |
| (), |
| ( |
| kernel::ReturnCode, |
| &'static mut <Self as kernel::hil::flash::Flash>::Page, |
| ), |
| > { |
| self.txbuffer.take().map(|txbuffer| { |
| self.rxbuffer.take().map(move |rxbuffer| { |
| txbuffer[0] = Opcodes::Read as u8; |
| // TODO(atv): See if we can make 4-byte addressing work. Otherwise limited to 2**24 bytes. |
| let byte = page * PAGE_SIZE; |
| txbuffer[1] = ((byte >> 16) & 0xFF) as u8; |
| txbuffer[2] = ((byte >> 08) & 0xFF) as u8; |
| txbuffer[3] = ((byte >> 00) & 0xFF) as u8; |
| self.state.set(State::WriteOpcode); |
| self.spi.hold_low(); |
| self.spi |
| .read_write_bytes(txbuffer, Some(rxbuffer), 4 as usize); |
| }); |
| }); |
| self.client_page.replace(read_buffer); |
| Ok(()) |
| } |
| fn write_page( |
| &self, |
| _: usize, |
| _: &'static mut <Self as kernel::hil::flash::Flash>::Page, |
| ) -> core::result::Result< |
| (), |
| ( |
| kernel::ReturnCode, |
| &'static mut <Self as kernel::hil::flash::Flash>::Page, |
| ), |
| > { |
| todo!() |
| } |
| fn erase_page(&self, _: usize) -> kernel::ReturnCode { |
| todo!() |
| } |
| } |
| |
| impl<'a, S: hil::spi::SpiMasterDevice + 'a> hil::time::AlarmClient for NexusSpiflash<'a, S> { |
| fn alarm(&self) { |
| todo!() |
| } |
| } |
| |
| impl<'a, S: hil::spi::SpiMasterDevice + 'a> hil::spi::SpiMasterClient for NexusSpiflash<'a, S> { |
| fn read_write_done( |
| &self, |
| write_buffer: &'static mut [u8], |
| read_buffer: core::option::Option<&'static mut [u8]>, |
| _len: usize, |
| ) { |
| match self.state.get() { |
| State::WriteOpcode => { |
| read_buffer.map(move |read_buffer| { |
| self.state.set(State::ReadPage { offset: 0 }); |
| self.spi |
| .read_write_bytes(write_buffer, Some(read_buffer), PAGE_SIZE / 2); |
| }); |
| } |
| State::ReadPage { offset } => { |
| read_buffer.map(move |read_buffer| { |
| self.client_page.map(|page| { |
| for i in 0..(PAGE_SIZE / 2) { |
| page[i + offset] = read_buffer[i]; |
| } |
| }); |
| let next_offset = offset + (PAGE_SIZE / 2); |
| if next_offset == PAGE_SIZE { |
| self.state.set(State::Idle); |
| self.client_page.take().map(|page| { |
| self.client.map(move |client| { |
| self.txbuffer.replace(read_buffer); |
| self.rxbuffer.replace(write_buffer); |
| client.read_complete(page, hil::flash::Error::CommandComplete); |
| }); |
| }); |
| } else { |
| self.state.set(State::ReadPage { |
| offset: next_offset, |
| }); |
| // release_low will cause our SPI host to release CS after the read/write. |
| if next_offset + (PAGE_SIZE / 2) == PAGE_SIZE { |
| self.spi.release_low(); |
| } |
| self.spi |
| .read_write_bytes(write_buffer, Some(read_buffer), PAGE_SIZE / 2); |
| } |
| }); |
| } |
| State::Idle => { |
| panic!(); |
| } |
| } |
| } |
| } |