├── .gitignore ├── dev ├── resettable_stream.rs ├── Cargo.toml ├── lib.rs ├── debug.rs ├── rng.rs ├── cs43l22.rs ├── circular_buffer.rs ├── usart.rs ├── htu21d.rs ├── i2c.rs └── esp8266.rs ├── .cargo └── config ├── openocd ├── openocd.tcl ├── stm32f407-discovery.cfg ├── stm32f429-discovery.cfg └── board │ └── stm32f429-discovery.cfg ├── linkmem ├── Cargo.toml └── src │ └── lib.rs ├── openocd.gdb ├── stm32f4 ├── Cargo.toml ├── lang_items.rs ├── crc.rs ├── nvic.rs ├── lib.rs ├── gpio.rs ├── timer.rs ├── rng.rs ├── volatile.rs ├── isr_vector.rs ├── usart.rs └── rcc.rs ├── smalloc └── Cargo.toml ├── breactor ├── Cargo.toml └── src │ ├── waker.rs │ ├── start_send_all_string.rs │ ├── start_send_all.rs │ ├── mutex.rs │ ├── promise.rs │ └── lib.rs ├── default.nix ├── .travis.yml ├── Cargo.toml ├── peripherals.ld ├── src ├── log.rs ├── led.rs ├── led_music.rs ├── terminal.rs └── main.rs ├── nix └── rust-nightly │ └── default.nix ├── stm32_flash.ld ├── Makefile ├── tests └── test.exp ├── README.md ├── isr_vector.ld ├── Cargo.lock └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /kernel.bin 3 | -------------------------------------------------------------------------------- /dev/resettable_stream.rs: -------------------------------------------------------------------------------- 1 | pub trait ResettableStream { 2 | fn reset(&mut self); 3 | } 4 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | # [build] 2 | # target = "thumbv7em-none-eabi" 3 | 4 | [target.thumbv7em-none-eabi] 5 | rustflags = ["-C", "link-arg=-Tstm32_flash.ld"] 6 | runner = "gdb -x openocd.gdb" -------------------------------------------------------------------------------- /openocd/openocd.tcl: -------------------------------------------------------------------------------- 1 | proc flash_bkernel { filename } { 2 | poll 3 | reset halt 4 | flash probe 0 5 | flash write_image erase $filename 0x08000000 6 | reset 7 | } 8 | -------------------------------------------------------------------------------- /linkmem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linkmem" 3 | version = "0.1.0" 4 | authors = ["Alexey Shmalko "] 5 | edition = "2018" 6 | 7 | [dependencies.smalloc] 8 | path = "../smalloc" 9 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | 3 | set print asm-demangle on 4 | 5 | break __isr__default 6 | break rust_begin_unwind 7 | 8 | monitor arm semihosting enable 9 | 10 | load 11 | 12 | stepi -------------------------------------------------------------------------------- /openocd/stm32f407-discovery.cfg: -------------------------------------------------------------------------------- 1 | source [find board/stm32f4discovery.cfg] 2 | source [find openocd.tcl] 3 | 4 | reset_config srst_only srst_nogate 5 | 6 | init 7 | reset init 8 | halt 9 | poll 10 | -------------------------------------------------------------------------------- /openocd/stm32f429-discovery.cfg: -------------------------------------------------------------------------------- 1 | source [find board/stm32f429-discovery.cfg] 2 | source [find openocd.tcl] 3 | 4 | reset_config srst_only srst_nogate 5 | 6 | init 7 | reset init 8 | halt 9 | poll 10 | -------------------------------------------------------------------------------- /stm32f4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stm32f4" 3 | version = "0.0.1" 4 | authors = ["Alexey Shmalko "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "stm32f4" 9 | path = "lib.rs" 10 | -------------------------------------------------------------------------------- /smalloc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smalloc" 3 | version = "0.0.1" 4 | authors = ["Alexey Shmalko "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "smalloc" 9 | path = "lib.rs" 10 | 11 | [dev-dependencies] 12 | rand_isaac = "0.1.1" 13 | rand = "0.6.5" -------------------------------------------------------------------------------- /openocd/board/stm32f429-discovery.cfg: -------------------------------------------------------------------------------- 1 | interface hla 2 | hla_layout stlink 3 | hla_device_desc "ST-LINK/V2" 4 | 5 | # stm32f429 discovery 0483:374b 6 | hla_vid_pid 0x0483 0x374b 7 | 8 | transport select hla_swd 9 | 10 | source [find target/stm32f4x.cfg] 11 | 12 | reset_config srst_only 13 | -------------------------------------------------------------------------------- /breactor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "breactor" 3 | version = "0.0.1" 4 | authors = ["Alexey Shmalko "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | futures = { package = "futures-preview", version = "0.3.0-alpha.16", default-features = false } 9 | stm32f4 = { path = "../stm32f4" } -------------------------------------------------------------------------------- /dev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dev" 3 | version = "0.0.1" 4 | authors = ["Alexey Shmalko "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "dev" 9 | path = "lib.rs" 10 | 11 | [dependencies] 12 | breactor = { path = "../breactor" } 13 | futures = { package = "futures-preview", version = "0.3.0-alpha.16", default-features = false } 14 | stm32f4 = { path = "../stm32f4" } -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { system ? builtins.currentSystem }: 2 | 3 | let 4 | pkgs = import { inherit system; }; 5 | 6 | in with pkgs; { 7 | bkernelEnv = mkShell { 8 | name = "bkernel"; 9 | buildInputs = [ 10 | gdb-multitarget 11 | gnumake 12 | git 13 | rustup 14 | gcc-arm-embedded 15 | minicom 16 | openocd 17 | expect 18 | ]; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /stm32f4/lang_items.rs: -------------------------------------------------------------------------------- 1 | //! `lang_items` and functions needed to start Rust on bare metal. 2 | 3 | // `loop {}` can't be replaced with `panic!()` 4 | #![allow(clippy::empty_loop)] 5 | 6 | #[cfg(target_os = "none")] 7 | #[no_mangle] 8 | pub unsafe extern "C" fn __aeabi_unwind_cpp_pr0() -> ! { 9 | loop {} 10 | } 11 | 12 | #[cfg(target_os = "none")] 13 | #[no_mangle] 14 | pub unsafe extern "C" fn __aeabi_unwind_cpp_pr1() -> ! { 15 | loop {} 16 | } 17 | -------------------------------------------------------------------------------- /linkmem/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This module is a rust interface to smalloc. 2 | //! 3 | //! To get more info on custom allocators see: 4 | //! https://doc.rust-lang.org/nightly/book/custom-allocators.html 5 | 6 | #![no_std] 7 | 8 | extern crate smalloc; 9 | 10 | use smalloc::Smalloc; 11 | 12 | #[cfg_attr(not(test), global_allocator)] 13 | static mut ALLOCATOR: Smalloc = Smalloc { 14 | start: 0 as *mut u8, 15 | size: 0, 16 | }; 17 | 18 | pub fn init(alloc: Smalloc) { 19 | unsafe { 20 | ALLOCATOR = alloc; 21 | ALLOCATOR.init(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | install: 5 | - sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y 6 | - sudo apt-get update -qq 7 | - sudo apt-get install gcc-arm-embedded -y 8 | - rustup target add thumbv7em-none-eabi 9 | - rustup component add rustfmt 10 | script: 11 | - cargo test --all 12 | - cargo build --release --target thumbv7em-none-eabi 13 | # rustdoc fails with associated types: 14 | # https://github.com/rust-lang/rust/issues/58011 15 | # 16 | # - cargo doc --target thumbv7em-none-eabi 17 | - cargo fmt -- --check 18 | -------------------------------------------------------------------------------- /dev/lib.rs: -------------------------------------------------------------------------------- 1 | //! Future-based drivers for hardware peripherals. 2 | #![cfg_attr(not(test), no_std)] 3 | #![feature(const_fn)] 4 | #![feature(integer_atomics)] 5 | #![feature(core_intrinsics)] 6 | #![feature(fixed_size_array)] 7 | #![feature(existential_type)] 8 | 9 | extern crate breactor; 10 | #[macro_use] 11 | extern crate futures; 12 | extern crate stm32f4; 13 | 14 | mod circular_buffer; 15 | // #[cfg(test)] 16 | // mod debug; 17 | mod resettable_stream; 18 | 19 | pub mod cs43l22; 20 | pub mod esp8266; 21 | pub mod htu21d; 22 | pub mod i2c; 23 | pub mod rng; 24 | pub mod usart; 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bkernel" 3 | version = "0.0.1" 4 | authors = ["Alexey Shmalko "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | futures = { package = "futures-preview", version = "0.3.0-alpha.16", default-features = false } 9 | dev = { path = "./dev" } 10 | stm32f4 = { path = "./stm32f4" } 11 | smalloc = { path = "./smalloc" } 12 | linkmem = { path = "./linkmem" } 13 | breactor = { path = "./breactor" } 14 | 15 | [[bin]] 16 | name = "bkernel" 17 | 18 | [profile.release] 19 | opt-level = 3 20 | lto = true 21 | debug = true 22 | debug-assertions = false 23 | panic = 'abort' 24 | incremental = false 25 | 26 | [profile.dev] 27 | panic = 'abort' 28 | 29 | [workspace] 30 | members = [ 31 | "dev", 32 | "smalloc", 33 | "linkmem", 34 | "breactor", 35 | ] -------------------------------------------------------------------------------- /peripherals.ld: -------------------------------------------------------------------------------- 1 | RCC = 0x40023800; 2 | 3 | CRC = 0x40023000; 4 | 5 | GPIO_A = 0x40020000; 6 | GPIO_B = 0x40020400; 7 | GPIO_C = 0x40020800; 8 | GPIO_D = 0x40020C00; 9 | GPIO_E = 0x40021000; 10 | GPIO_F = 0x40021400; 11 | GPIO_G = 0x40021800; 12 | GPIO_H = 0x40021C00; 13 | GPIO_I = 0x40022000; 14 | GPIO_J = 0x40022400; 15 | GPIO_K = 0x40022800; 16 | 17 | USART1 = 0x40011000; 18 | USART2 = 0x40004400; 19 | USART3 = 0x40004800; 20 | 21 | I2C1 = 0x40005400; 22 | I2C2 = 0x40005800; 23 | I2C3 = 0x40005C00; 24 | 25 | TIM2 = 0x40000000; 26 | TIM3 = 0x40000400; 27 | TIM4 = 0x40000800; 28 | TIM5 = 0x40000C00; 29 | 30 | RNG = 0x50060800; 31 | 32 | ICTR = 0xE000E004; 33 | ISER = 0xE000E100; 34 | ICER = 0xE000E180; 35 | ISPR = 0xE000E200; 36 | ICPR = 0xE000E280; 37 | IABR = 0xE000E300; 38 | IPR = 0xE000E400; 39 | 40 | AIRCR = 0xE000ED0C; 41 | -------------------------------------------------------------------------------- /breactor/src/waker.rs: -------------------------------------------------------------------------------- 1 | use core::task::{RawWaker, RawWakerVTable, Waker}; 2 | 3 | use super::REACTOR; 4 | 5 | pub const WAKER_VTABLE: RawWakerVTable = 6 | RawWakerVTable::new(waker_clone, waker_wake, waker_wake_by_ref, waker_drop); 7 | 8 | unsafe fn waker_clone(data: *const ()) -> RawWaker { 9 | RawWaker::new(data, &WAKER_VTABLE) 10 | } 11 | 12 | unsafe fn waker_wake(data: *const ()) { 13 | let task_mask = data as u32; 14 | REACTOR.set_ready_task_mask(task_mask) 15 | } 16 | 17 | unsafe fn waker_wake_by_ref(data: *const ()) { 18 | let task_mask = data as u32; 19 | REACTOR.set_ready_task_mask(task_mask) 20 | } 21 | 22 | unsafe fn waker_drop(_data: *const ()) {} 23 | 24 | pub fn new_task_waker(task_mask: u32) -> Waker { 25 | unsafe { Waker::from_raw(RawWaker::new(task_mask as *const (), &WAKER_VTABLE)) } 26 | } 27 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | //! Logging. 2 | 3 | use ::dev::usart::Usart; 4 | 5 | use ::core::array::FixedSizeArray; 6 | 7 | #[allow(missing_debug_implementations)] 8 | pub struct Logger<'a, A: FixedSizeArray + 'a, B: FixedSizeArray + 'a> { 9 | inner: &'a Usart, 10 | } 11 | 12 | impl<'a, A: FixedSizeArray + 'a, B: FixedSizeArray + 'a> Logger<'a, A, B> { 13 | pub const fn new(usart: &Usart) -> Logger { 14 | Logger { inner: usart } 15 | } 16 | } 17 | 18 | /// This is very bad implementation for several reasons: 19 | /// 20 | /// 1. It fails when the buffer is full, printing only the first part 21 | /// of the string. 22 | /// 23 | /// 2. It requires getting a mutable reference to the buffer, which is 24 | /// not safe. 25 | impl<'a, A: FixedSizeArray, B: FixedSizeArray> ::core::fmt::Write for Logger<'a, A, B> { 26 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 27 | for b in s.as_bytes() { 28 | if !self.inner.try_push_writer(*b) { 29 | return Err(::core::fmt::Error); 30 | } 31 | } 32 | 33 | Ok(()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/led.rs: -------------------------------------------------------------------------------- 1 | use stm32f4::gpio; 2 | 3 | pub static LD3: Led = Led { 4 | gpio: unsafe { &gpio::GPIO_D }, 5 | pin: 13, 6 | }; 7 | pub static LD4: Led = Led { 8 | gpio: unsafe { &gpio::GPIO_D }, 9 | pin: 12, 10 | }; 11 | pub static LD5: Led = Led { 12 | gpio: unsafe { &gpio::GPIO_D }, 13 | pin: 14, 14 | }; 15 | pub static LD6: Led = Led { 16 | gpio: unsafe { &gpio::GPIO_D }, 17 | pin: 15, 18 | }; 19 | 20 | pub struct Led { 21 | gpio: &'static gpio::Gpio, 22 | pin: u32, 23 | } 24 | 25 | impl Led { 26 | pub fn init(&self) { 27 | self.gpio.enable( 28 | self.pin, 29 | gpio::GpioConfig { 30 | mode: gpio::GpioMode::OUTPUT, 31 | ospeed: gpio::GpioOSpeed::LOW_SPEED, 32 | otype: gpio::GpioOType::PUSH_PULL, 33 | pupd: gpio::GpioPuPd::NO, 34 | af: gpio::GpioAF::AF0, // not used 35 | }, 36 | ); 37 | } 38 | 39 | pub fn turn_on(&self) { 40 | self.gpio.set_bit(self.pin); 41 | } 42 | 43 | pub fn turn_off(&self) { 44 | self.gpio.clear_bit(self.pin); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/led_music.rs: -------------------------------------------------------------------------------- 1 | use crate::led; 2 | 3 | // TODO(rasen): rewrite this module to use Futures and timers. 4 | 5 | pub fn led_fun(tt: u32) { 6 | delay(tt); 7 | led::LD3.turn_off(); 8 | led::LD4.turn_off(); 9 | led::LD5.turn_off(); 10 | led::LD6.turn_off(); 11 | delay(tt); 12 | led::LD3.turn_on(); 13 | led::LD4.turn_on(); 14 | led::LD5.turn_on(); 15 | led::LD6.turn_on(); 16 | delay(tt); 17 | 18 | for _ in 0..10 { 19 | play_led_step(tt); 20 | } 21 | 22 | delay(tt); 23 | led::LD3.turn_on(); 24 | led::LD4.turn_on(); 25 | led::LD5.turn_on(); 26 | led::LD6.turn_on(); 27 | } 28 | 29 | fn delay(a: u32) { 30 | unsafe { 31 | let i: ::stm32f4::volatile::RW = ::core::mem::uninitialized(); 32 | i.set(a); 33 | while i.get() > 0 { 34 | i.update(|x| x - 1); 35 | } 36 | } 37 | } 38 | 39 | fn play_led_step(tt: u32) { 40 | led::LD3.turn_on(); 41 | delay(tt); 42 | led::LD3.turn_off(); 43 | 44 | delay(tt / 10); 45 | 46 | led::LD4.turn_on(); 47 | delay(tt); 48 | led::LD4.turn_off(); 49 | 50 | delay(tt / 10); 51 | 52 | led::LD5.turn_on(); 53 | delay(tt); 54 | led::LD5.turn_off(); 55 | 56 | delay(tt / 10); 57 | 58 | led::LD6.turn_on(); 59 | delay(1000); 60 | led::LD6.turn_off(); 61 | 62 | delay(1000); 63 | } 64 | -------------------------------------------------------------------------------- /dev/debug.rs: -------------------------------------------------------------------------------- 1 | use futures::prelude::*; 2 | use std::collections::VecDeque; 3 | 4 | use crate::resettable_stream::ResettableStream; 5 | 6 | /// A `Sink + Stream` implementation backed by `Vec` and `VecDeque`. Should only be used for 7 | /// testing. 8 | pub struct TestChannel { 9 | sink: Vec, 10 | stream: VecDeque, 11 | } 12 | 13 | impl TestChannel { 14 | pub fn new() -> TestChannel { 15 | TestChannel { 16 | sink: Vec::new(), 17 | stream: VecDeque::new(), 18 | } 19 | } 20 | 21 | pub fn sink(&self) -> &Vec { 22 | &self.sink 23 | } 24 | 25 | pub fn stream(&mut self) -> &mut VecDeque { 26 | &mut self.stream 27 | } 28 | } 29 | 30 | impl Sink for TestChannel { 31 | type SinkError = (); 32 | 33 | fn start_send(&mut self, item: T) -> StartSend { 34 | self.sink.push(item); 35 | Ok(AsyncSink::Ready) 36 | } 37 | 38 | fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { 39 | Ok(Async::Ready(())) 40 | } 41 | 42 | fn close(&mut self) -> Poll<(), Self::SinkError> { 43 | self.poll_complete() 44 | } 45 | } 46 | 47 | impl Stream for TestChannel { 48 | type Item = T; 49 | type Error = (); 50 | 51 | fn poll(&mut self) -> Poll, Self::Error> { 52 | Ok(self.stream.pop_front().into()) 53 | } 54 | } 55 | 56 | impl ResettableStream for TestChannel { 57 | fn reset(&mut self) {} 58 | } 59 | -------------------------------------------------------------------------------- /nix/rust-nightly/default.nix: -------------------------------------------------------------------------------- 1 | { date, hash 2 | , stdenv, fetchurl, zlib }: 3 | 4 | let 5 | target = 6 | if stdenv.system == "i686-linux" then "i686-unknown-linux-gnu" else 7 | if stdenv.system == "x86_64-linux" then "x86_64-unknown-linux-gnu" else 8 | if stdenv.system == "i686-darwin" then "i868-apple-darwin" else 9 | if stdenv.system == "x86_64-darwin" then "x86_64-apple-darwin" else 10 | abort "no snapshot to bootstrap for this platfrom (missing target triple)"; 11 | 12 | in stdenv.mkDerivation rec { 13 | name = "rust-nightly-${date}"; 14 | 15 | src = fetchurl { 16 | url = "https://static.rust-lang.org/dist/${date}/rust-nightly-${target}.tar.gz"; 17 | sha256 = hash; 18 | }; 19 | 20 | installPhase = '' 21 | ./install.sh --prefix=$out --disable-ldconfig --without=rust-docs 22 | ''; 23 | 24 | dontStrip = true; 25 | 26 | preFixup = if stdenv.isLinux then let 27 | rpath = stdenv.lib.concatStringsSep ":" [ 28 | "$out/lib" 29 | (stdenv.lib.makeLibraryPath [ zlib ]) 30 | ''${stdenv.cc.cc}/lib${stdenv.lib.optionalString stdenv.is64bit "64"}'' 31 | ]; 32 | in 33 | '' 34 | for executable in ${stdenv.lib.concatMapStringsSep " " (s: "$out/bin/" + s) [ "cargo" "rustc" "rustdoc" ]}; do 35 | patchelf --interpreter "${stdenv.glibc.out}/lib/${stdenv.cc.dynamicLinker}" \ 36 | --set-rpath "${rpath}" \ 37 | "$executable" 38 | done 39 | for library in $out/lib/*.so; do 40 | patchelf --set-rpath "${rpath}" "$library" 41 | done 42 | '' else ""; 43 | } 44 | -------------------------------------------------------------------------------- /stm32_flash.ld: -------------------------------------------------------------------------------- 1 | INCLUDE isr_vector.ld; 2 | INCLUDE peripherals.ld; 3 | 4 | __stack_end = 0x20020000; /* end of 128K RAM on AHB bus*/ 5 | 6 | MEMORY 7 | { 8 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K 9 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 10 | MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K 11 | } 12 | 13 | SECTIONS 14 | { 15 | .isr_vector : 16 | { 17 | . = ALIGN(4); 18 | KEEP(*(.stack_end)) 19 | KEEP(*(.isr_vector)) 20 | . = ALIGN(4); 21 | } >FLASH 22 | 23 | .text : 24 | { 25 | . = ALIGN(4); 26 | *(.text) 27 | *(.text*) 28 | *(.rodata) 29 | *(.rodata*) 30 | *(.glue_7) 31 | *(.glue_7t) 32 | *(.eh_frame) 33 | 34 | KEEP (*(.init)) 35 | KEEP (*(.fini)) 36 | 37 | . = ALIGN(4); 38 | } >FLASH 39 | 40 | 41 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 42 | .ARM : { 43 | __exidx_start = .; 44 | *(.ARM.exidx*) 45 | __exidx_end = .; 46 | } >FLASH 47 | 48 | . = ALIGN(4); 49 | __init_data_start = .; 50 | 51 | .data : AT ( __init_data_start ) 52 | { 53 | . = ALIGN(4); 54 | __data_start = .; 55 | *(.data) 56 | *(.data*) 57 | 58 | . = ALIGN(4); 59 | __data_end = .; 60 | } >RAM 61 | 62 | .bss : 63 | { 64 | . = ALIGN(4); 65 | __bss_start = .; 66 | *(.bss) 67 | *(.bss*) 68 | *(COMMON) 69 | 70 | . = ALIGN(4); 71 | __bss_end = .; 72 | } >RAM 73 | 74 | .ARM.attributes 0 : { *(.ARM.attributes) } 75 | } 76 | -------------------------------------------------------------------------------- /breactor/src/start_send_all_string.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::task::Context; 3 | use futures::{Future, Poll, Sink}; 4 | 5 | #[derive(Debug)] 6 | #[must_use = "futures do nothing unless polled"] 7 | pub struct StartSendAllString<'a, T> { 8 | sink: Option, 9 | string: &'a str, 10 | cur: usize, 11 | } 12 | 13 | impl<'a, T> Unpin for StartSendAllString<'a, T> where T: Sink + Unpin {} 14 | 15 | impl<'a, T> StartSendAllString<'a, T> 16 | where 17 | T: Sink + Unpin, 18 | { 19 | pub fn new(sink: T, string: &'a str) -> StartSendAllString<'a, T> { 20 | StartSendAllString { 21 | sink: Some(sink), 22 | string, 23 | cur: 0, 24 | } 25 | } 26 | } 27 | 28 | impl<'a, T> StartSendAllString<'a, T> 29 | where 30 | T: Sink, 31 | { 32 | fn sink_mut(&mut self) -> &mut T { 33 | self.sink.as_mut().take().expect("") 34 | } 35 | 36 | fn take_result(&mut self) -> T { 37 | self.sink.take().expect("") 38 | } 39 | } 40 | 41 | impl<'a, T> Future for StartSendAllString<'a, T> 42 | where 43 | T: Sink + Unpin, 44 | { 45 | type Output = Result; 46 | 47 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 48 | let this = &mut *self; 49 | 50 | while this.cur < this.string.as_bytes().len() { 51 | try_ready!(Pin::new(this.sink_mut()).poll_ready(cx)); 52 | 53 | let item = this.string.as_bytes()[this.cur]; 54 | Pin::new(this.sink_mut()).start_send(item)?; 55 | 56 | this.cur += 1; 57 | } 58 | 59 | Poll::Ready(Ok(self.take_result())) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /stm32f4/crc.rs: -------------------------------------------------------------------------------- 1 | //! CRC calculation unit. 2 | use crate::volatile::{RW, WO}; 3 | 4 | extern "C" { 5 | pub static CRC: Crc; 6 | } 7 | 8 | /// Don't forget to enable CRC peripheral before use. 9 | /// 10 | /// ```no_run 11 | /// # use stm32f4::rcc; 12 | /// unsafe { 13 | /// rcc::RCC.ahb1_clock_enable(rcc::Ahb1Enable::CRC); 14 | /// } 15 | /// ``` 16 | #[repr(C)] 17 | #[allow(missing_debug_implementations)] 18 | pub struct Crc { 19 | dr: RW, // 0x0 20 | idr: RW, // 0x4 21 | cr: WO, // 0x8 22 | } 23 | 24 | impl Crc { 25 | /// Resets the CRC Data register (DR). 26 | pub fn reset(&self) { 27 | unsafe { 28 | self.cr.set(0x1); 29 | } 30 | } 31 | 32 | /// Computes the 32-bit CRC of a given data word (32-bit). 33 | pub fn calculate_crc(&self, data: u32) -> u32 { 34 | unsafe { 35 | self.dr.set(data); 36 | self.dr.get() 37 | } 38 | } 39 | 40 | /// Returns the current CRC value. 41 | pub fn get_crc(&self) -> u32 { 42 | unsafe { self.dr.get() } 43 | } 44 | 45 | /// Stores 8-bit value in the Independent Data Register. 46 | pub fn set_idr(&self, value: u8) { 47 | unsafe { 48 | self.idr.set(u32::from(value)); 49 | } 50 | } 51 | 52 | /// Reads 8-bit value from the Indenpendent Data Register. 53 | #[allow(clippy::cast_possible_truncation)] // IDR is 8-bit register 54 | pub fn get_idr(&self) -> u8 { 55 | unsafe { self.idr.get() as u8 } 56 | } 57 | 58 | pub fn block_crc(&self, data: &[u32]) -> u32 { 59 | unsafe { 60 | for x in data { 61 | self.dr.set(*x); 62 | } 63 | self.dr.get() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /dev/rng.rs: -------------------------------------------------------------------------------- 1 | //! Random number generator. 2 | use core::pin::Pin; 3 | use core::sync::atomic::{AtomicU32, Ordering}; 4 | use core::task::Context; 5 | 6 | use stm32f4::rng; 7 | use stm32f4::IrqLock; 8 | 9 | use breactor::REACTOR; 10 | 11 | use futures::{Poll, Stream}; 12 | 13 | pub static mut RNG: Rng = Rng { 14 | inner: unsafe { &rng::RNG }, 15 | task: AtomicU32::new(0), 16 | }; 17 | 18 | #[allow(missing_debug_implementations)] 19 | pub struct Rng<'a> { 20 | inner: &'a rng::Rng, 21 | task: AtomicU32, 22 | } 23 | 24 | impl<'a> Rng<'a> { 25 | pub fn enable(&self) { 26 | self.inner.enable(); 27 | } 28 | 29 | pub fn disable(&self) { 30 | self.inner.disable(); 31 | } 32 | } 33 | 34 | impl<'a> Stream for Rng<'a> { 35 | type Item = Result; 36 | 37 | fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { 38 | let task = REACTOR.get_current_task_mask(); 39 | 40 | self.task.fetch_or(task, Ordering::SeqCst); 41 | 42 | // TODO(rasen): disable RNG interrupt only? 43 | let _lock = unsafe { IrqLock::new() }; 44 | match self.inner.get() { 45 | Ok(Some(x)) => { 46 | self.task.fetch_and(!task, Ordering::SeqCst); 47 | Poll::Ready(Some(Ok(x))) 48 | } 49 | Err(err) => { 50 | self.task.fetch_and(!task, Ordering::SeqCst); 51 | Poll::Ready(Some(Err(err))) 52 | } 53 | Ok(None) => { 54 | self.inner.it_enable(); 55 | Poll::Pending 56 | } 57 | } 58 | } 59 | } 60 | 61 | #[no_mangle] 62 | pub unsafe extern "C" fn __isr_hash_rng() { 63 | let task = RNG.task.swap(0, Ordering::SeqCst); 64 | REACTOR.set_ready_task_mask(task); 65 | RNG.inner.it_disable(); 66 | } 67 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := thumbv7em-none-eabi 2 | 3 | BOARD ?= stm32f407-discovery 4 | 5 | RUSTCFLAGS := ${RUSTCFLAGS} -g --target $(TARGET) -C opt-level=3 6 | 7 | CLIPPYFLAGS := \ 8 | -A unknown_lints \ 9 | -A char_lit_as_u8 \ 10 | -A inline_always \ 11 | -A identity_op \ 12 | -A doc_markdown \ 13 | -A empty_loop \ 14 | -W cast_possible_wrap \ 15 | -W cast_sign_loss \ 16 | -W float_arithmetic \ 17 | -W non_ascii_literal \ 18 | -W nonminimal_bool \ 19 | -W result_unwrap_used \ 20 | -W shadow_unrelated \ 21 | -W similar_names \ 22 | -W unseparated_literal_suffix \ 23 | -W used_underscore_binding \ 24 | -W wrong_pub_self_convention \ 25 | -W cast_possible_truncation \ 26 | 27 | DEVICE ?= /dev/ttyUSB0 28 | 29 | .PHONY: all 30 | all: build doc test 31 | du -b kernel.bin 32 | 33 | .PHONY: build 34 | build: kernel.bin 35 | 36 | kernel.bin: target/$(TARGET)/release/bkernel $(LD_SOURCES) # kernel.elf 37 | $(OBJCOPY) -O binary $< $@ 38 | 39 | target/$(TARGET)/release/bkernel: $(SOURCES) 40 | RUSTFLAGS="${RUSTFLAGS}" cargo build --target=$(TARGET) --release 41 | 42 | .PHONY: doc 43 | doc: 44 | RUSTFLAGS="${RUSTFLAGS}" cargo doc --target=$(TARGET) 45 | 46 | .PHONY: test 47 | test: 48 | RUSTFLAGS="${RUSTFLAGS}" cargo test -p bkernel -p stm32f4 -p breactor -p smalloc -p dev 49 | 50 | .PHONY: clippy 51 | clippy: 52 | cargo clean 53 | RUSTFLAGS="${RUSTFLAGS}" cargo clippy --target=thumbv7em-none-eabi -p stm32f4 -- ${CLIPPYFLAGS} 54 | RUSTFLAGS="${RUSTFLAGS}" cargo clippy --target=thumbv7em-none-eabi -p breactor -- ${CLIPPYFLAGS} 55 | RUSTFLAGS="${RUSTFLAGS}" cargo clippy --target=thumbv7em-none-eabi -p smalloc -- ${CLIPPYFLAGS} 56 | RUSTFLAGS="${RUSTFLAGS}" cargo clippy --target=thumbv7em-none-eabi -p linkmem -- ${CLIPPYFLAGS} 57 | RUSTFLAGS="${RUSTFLAGS}" cargo clippy --target=thumbv7em-none-eabi -p dev -- ${CLIPPYFLAGS} 58 | RUSTFLAGS="${RUSTFLAGS}" cargo clippy --target=thumbv7em-none-eabi -p bkernel -- ${CLIPPYFLAGS} 59 | 60 | .PHONY: openocd 61 | openocd: 62 | openocd -f ${BOARD}.cfg -s openocd/ 63 | 64 | .PHONY: flash 65 | flash: kernel.bin 66 | openocd -f ${BOARD}.cfg -s openocd/ -c 'flash_bkernel kernel.bin; exit' 67 | 68 | .PHONY: reset 69 | reset: 70 | openocd -f ${BOARD}.cfg -s openocd/ -c 'reset; exit' 71 | 72 | .PHONY: device_test 73 | device_test: 74 | expect tests/test.exp $(DEVICE) 75 | 76 | .PHONY: clean 77 | clean: 78 | rm -rf *.elf *.bin lib 79 | cargo clean 80 | -------------------------------------------------------------------------------- /tests/test.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env expect 2 | 3 | # exp_internal 1 4 | log_user 0 5 | 6 | if {$argc != 1} { 7 | puts "usage: test.exp port" 8 | exit 9 | } 10 | 11 | if {[info exists env(BOARD)]} { 12 | set BOARD $env(BOARD) 13 | } else { 14 | set BOARD "stm32f407-discovery" 15 | } 16 | 17 | set port $argv 18 | set spawned [spawn -open [open $port w+]] 19 | set baud 115200 20 | set send_human { .01 .01 100 .005 .005 } 21 | 22 | stty ispeed $baud ospeed $baud raw -echo cs8 -parenb -cstopb onlcr < $port 23 | 24 | proc test_flow {str} { 25 | if {[log_user -info] == 0} { send_user $str } 26 | } 27 | 28 | proc test {name} { 29 | global current_test 30 | set current_test $name 31 | test_flow "test $name ... " 32 | } 33 | 34 | proc green {str} { 35 | return "\033\[1;32m$str\033\[0m" 36 | } 37 | 38 | proc red {str} { 39 | return "\033\[1;31m$str\033\[0m" 40 | } 41 | 42 | proc test_done { } { 43 | global current_test 44 | unset current_test 45 | test_flow [green "ok\n"] 46 | } 47 | 48 | proc reset {} { 49 | global BOARD 50 | system "openocd -s openocd/ -f $BOARD.cfg -c 'reset; exit' 2> /dev/null" 51 | } 52 | 53 | proc expect_exact {pattern} { 54 | expect { 55 | default { 56 | test_flow [red "FAIL\n"] 57 | error "\n\nTimed out waiting for \"$pattern\"\n"; 58 | exit 1; 59 | } 60 | "^$pattern" 61 | } 62 | } 63 | 64 | proc expect_prompt {} { 65 | expect_exact "> " 66 | } 67 | 68 | proc send_command {cmd} { 69 | send -h -- "$cmd\r" 70 | expect "\n" 71 | } 72 | 73 | send_user "\n" 74 | 75 | test "welcome prompt" 76 | reset 77 | expect_exact "*Welcome to bkernel!\r\n" 78 | expect_exact "Type 'help' to get a list of available commands.\r\n" 79 | expect_prompt 80 | test_done 81 | 82 | test "hi" 83 | send_command "hi" 84 | expect_exact "Hi, there!\r\n" 85 | expect_prompt 86 | test_done 87 | 88 | test "LED" 89 | send_command "-3" 90 | expect_prompt 91 | send_command "-4" 92 | expect_prompt 93 | send_command "-5" 94 | expect_prompt 95 | send_command "-6" 96 | expect_prompt 97 | 98 | send_command "+3" 99 | expect_prompt 100 | send_command "+4" 101 | expect_prompt 102 | send_command "+5" 103 | expect_prompt 104 | send_command "+6" 105 | expect_prompt 106 | test_done 107 | 108 | test "unknown command" 109 | send_command "+7" 110 | expect_exact "Unknown command\r\n" 111 | expect_prompt 112 | test_done 113 | 114 | send_user "\nAll tests are successful\n" 115 | -------------------------------------------------------------------------------- /breactor/src/start_send_all.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::stream::Fuse; 3 | use futures::stream::StreamExt; 4 | use futures::task::Context; 5 | use futures::{Future, Poll, Sink, Stream}; 6 | 7 | #[derive(Debug)] 8 | #[must_use = "futures do nothing unless polled"] 9 | pub struct StartSendAll { 10 | sink: Option, 11 | stream: Option>, 12 | buffered: Option, 13 | } 14 | 15 | impl Unpin for StartSendAll 16 | where 17 | Si: Sink + Unpin, 18 | St: Stream + Unpin, 19 | { 20 | } 21 | 22 | #[allow(dead_code)] 23 | pub fn new(sink: Si, stream: St) -> StartSendAll 24 | where 25 | Si: Sink, 26 | St: Stream, 27 | { 28 | StartSendAll { 29 | sink: Some(sink), 30 | stream: Some(stream.fuse()), 31 | buffered: None, 32 | } 33 | } 34 | 35 | impl StartSendAll 36 | where 37 | Si: Sink + Unpin, 38 | St: Stream + Unpin, 39 | { 40 | fn sink_mut(&mut self) -> &mut Si { 41 | self.sink.as_mut().take().expect("") 42 | } 43 | 44 | fn stream_mut(&mut self) -> &mut Fuse { 45 | self.stream.as_mut().take().expect("") 46 | } 47 | 48 | fn take_result(&mut self) -> (Si, St) { 49 | let sink = self.sink.take().expect(""); 50 | let fuse = self.stream.take().expect(""); 51 | (sink, fuse.into_inner()) 52 | } 53 | 54 | fn try_start_send( 55 | &mut self, 56 | cx: &mut Context<'_>, 57 | item: St::Item, 58 | ) -> Poll> { 59 | debug_assert!(self.buffered.is_none()); 60 | match Pin::new(self.sink_mut()).poll_ready(cx) { 61 | Poll::Ready(Ok(())) => Poll::Ready(Pin::new(self.sink_mut()).start_send(item)), 62 | Poll::Ready(Err(e)) => Poll::Ready(Err(e)), 63 | Poll::Pending => { 64 | self.buffered = Some(item); 65 | Poll::Pending 66 | } 67 | } 68 | } 69 | } 70 | 71 | impl Future for StartSendAll 72 | where 73 | Si: Sink + Unpin, 74 | St: Stream + Unpin, 75 | { 76 | type Output = Result<(Si, St), Si::SinkError>; 77 | 78 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 79 | let this = &mut *self; 80 | 81 | // If we've got an item buffered already, we need to write it to the 82 | // sink before we can do anything else 83 | if let Some(item) = this.buffered.take() { 84 | ready!(this.try_start_send(cx, item))? 85 | } 86 | 87 | loop { 88 | match Pin::new(this.stream_mut()).poll_next(cx) { 89 | Poll::Ready(Some(item)) => try_ready!(this.try_start_send(cx, item)), 90 | Poll::Ready(None) => return Poll::Ready(Ok(this.take_result())), 91 | Poll::Pending => return Poll::Pending, 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /dev/cs43l22.rs: -------------------------------------------------------------------------------- 1 | //! CS43L22 Low Power, Stereo DAC with Headphone and Speaker Amplifiers. 2 | use crate::i2c; 3 | 4 | use futures::{Future, FutureExt, TryFutureExt}; 5 | 6 | #[allow(missing_debug_implementations)] 7 | pub struct Cs43l22 { 8 | i2c: &'static i2c::I2cBus, 9 | i2c_addr: u16, 10 | buffer: [u8; 8], 11 | } 12 | 13 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 14 | pub enum Error { 15 | /// An I2C error has occured. 16 | I2cError(i2c::Error), 17 | } 18 | 19 | impl From for Error { 20 | fn from(err: i2c::Error) -> Error { 21 | Error::I2cError(err) 22 | } 23 | } 24 | 25 | #[allow(dead_code)] 26 | #[allow(non_camel_case_types)] 27 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 28 | #[repr(u8)] 29 | enum Register { 30 | // 0x0: Reserved 31 | ID = 0x1, 32 | PowerCtl1 = 0x2, 33 | // 0x3: Reserved 34 | PowerCtl2 = 0x4, 35 | ClockingCtl = 0x5, 36 | InterfaceCtl1 = 0x6, 37 | InterfaceCtl2 = 0x7, 38 | PassthroughASelect = 0x8, 39 | PassthroughBSelect = 0x9, 40 | AnalogZCAndSRSettings = 0xA, 41 | // 0xB: Reserved 42 | PassthroughGangControl = 0xC, 43 | PlaybackCtl1 = 0xD, 44 | MiscCtl = 0xE, 45 | PlaybackCtl2 = 0xF, 46 | // 0x10 -- 0x13: Reserved 47 | PassthroughAVol = 0x14, 48 | PassthroughBVol = 0x15, 49 | // 0x16 -- 0x19: Reserved 50 | PCMAVol = 0x1A, 51 | PCMBVol = 0x1B, 52 | BEEPFreq_OnTime = 0x1C, 53 | BEEPFVol_OffTime = 0x1D, 54 | BEEP_ToneCfg = 0x1E, 55 | ToneCtl = 0x1F, 56 | MasterAVol = 0x20, 57 | MasterBVol = 0x21, 58 | HeadphoneAVol = 0x22, 59 | HeadphoneBVol = 0x23, 60 | SpeakerAVol = 0x24, 61 | SpeakerBVol = 0x25, 62 | ChannelMixer_Swap = 0x26, 63 | LimitCtl1_Thresholds = 0x27, 64 | LimitCtl2_ReleaseRate = 0x28, 65 | LimiterAttackRate = 0x29, 66 | // 0x2A -- 0x2D: Reserved 67 | Overflow_ClockStatus = 0x2E, 68 | BatteryCompensation = 0x2F, 69 | VPBatteryLevel = 0x30, 70 | SpeakerStatus = 0x31, 71 | // 0x32 -- 0x33: Reserved 72 | ChargePumpFrequency = 0x34, 73 | } 74 | 75 | impl Cs43l22 { 76 | /// Create new Cs43l22 instance. 77 | /// 78 | /// `ad0` is the LSB of the chip address. 79 | /// 80 | /// ## Example 81 | /// ```no_run 82 | /// let cs43l22 = dev::cs43l22::Cs43l22::new(&dev::i2c::I2C1_BUS, false); 83 | /// ``` 84 | pub const fn new(i2c: &'static i2c::I2cBus, ad0: bool) -> Cs43l22 { 85 | Cs43l22 { 86 | i2c, 87 | i2c_addr: 0b1001_0100 | ((ad0 as u16) << 1), 88 | buffer: [0; 8], 89 | } 90 | } 91 | 92 | pub fn get_chip_id(&'static mut self) -> impl Future> + 'static { 93 | let addr = self.i2c_addr; 94 | 95 | self.buffer[0] = 0x01; // ID register 96 | let buffer = self.buffer.as_mut_ptr(); 97 | 98 | self.i2c 99 | .start_transfer() 100 | .then(move |i2c| i2c.master_transmitter_raw(addr, buffer, 1)) 101 | .and_then(move |(i2c, _buffer)| i2c.master_receiver_raw(addr, buffer, 1)) 102 | .map_ok(|(mut i2c, buffer)| { 103 | i2c.stop(); 104 | buffer[0] 105 | }) 106 | .map_err(Error::I2cError) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /stm32f4/nvic.rs: -------------------------------------------------------------------------------- 1 | //! Nested Vector Interrupt Controller 2 | 3 | use crate::volatile::{RO, RW}; 4 | 5 | extern "C" { 6 | pub static ICTR: RO; 7 | pub static ISER: [RW; 8]; 8 | pub static ICER: [RW; 8]; 9 | pub static ISPR: [RW; 8]; 10 | pub static ICPR: [RW; 8]; 11 | pub static IABR: [RO; 8]; 12 | pub static IPR: [RW; 82]; 13 | 14 | pub static AIRCR: RW; 15 | } 16 | 17 | #[derive(Debug)] 18 | pub struct NvicInit { 19 | pub irq_channel: IrqChannel, 20 | pub priority: u8, 21 | pub subpriority: u8, 22 | pub enable: bool, 23 | } 24 | 25 | #[allow(non_camel_case_types)] 26 | #[derive(Copy, Clone, Debug)] 27 | #[repr(u32)] 28 | pub enum IrqChannel { 29 | WWDG = 0, 30 | PVD = 1, 31 | TAMP_STAMP = 2, 32 | RTC_WKUP = 3, 33 | FLASH = 4, 34 | RCC = 5, 35 | EXTI0 = 6, 36 | EXTI1 = 7, 37 | EXTI2 = 8, 38 | EXTI3 = 9, 39 | EXTI4 = 10, 40 | DMA1_Stream0 = 11, 41 | DMA1_Stream1 = 12, 42 | DMA1_Stream2 = 13, 43 | DMA1_Stream3 = 14, 44 | DMA1_Stream4 = 15, 45 | DMA1_Stream5 = 16, 46 | DMA1_Stream6 = 17, 47 | ADC = 18, 48 | CAN1_TX = 19, 49 | CAN1_RX0 = 20, 50 | CAN1_RX1 = 21, 51 | CAN1_SCE = 22, 52 | EXTI9_5 = 23, 53 | TIM1_BRK_TIM9 = 24, 54 | TIM1_UP_TIM1 = 25, 55 | TIM1_TRG_COM_TIM11 = 26, 56 | TIM1_CC = 27, 57 | TIM2 = 28, 58 | TIM3 = 29, 59 | TIM4 = 30, 60 | I2C1_EV = 31, 61 | I2C1_ER = 32, 62 | I2C2_EV = 33, 63 | I2C2_ER = 34, 64 | SPI1 = 35, 65 | SPI2 = 36, 66 | USART1 = 37, 67 | USART2 = 38, 68 | USART3 = 39, 69 | EXTI15_10 = 40, 70 | RCT_Alarm = 41, 71 | OTG_FS_WKUP = 42, 72 | TIM8_BRK_TIM12 = 43, 73 | TIM8_UP_TIM13 = 44, 74 | TIM8_TRG_COM_TIM14 = 45, 75 | TIM8_CC = 46, 76 | DMA1_Stream7 = 47, 77 | FSMC = 48, 78 | SDIO = 49, 79 | TIM5 = 50, 80 | SPI3 = 51, 81 | UART4 = 52, 82 | UART5 = 53, 83 | TIM6_DAC = 54, 84 | TIM7 = 55, 85 | DMA2_Stream0 = 56, 86 | DMA2_Stream1 = 57, 87 | DMA2_Stream2 = 58, 88 | DMA2_Stream3 = 59, 89 | DMA2_Stream4 = 60, 90 | ETH = 61, 91 | ETH_WKUP = 62, 92 | CAN2_TX = 63, 93 | CAN2_RX0 = 64, 94 | CAN2_RX1 = 65, 95 | CAN2_SCE = 66, 96 | OTG_FS = 67, 97 | DMA2_Stream5 = 68, 98 | DMA2_Stream6 = 69, 99 | DMA2_Stream7 = 70, 100 | USART6 = 71, 101 | I2C3_EV = 72, 102 | I2C3_ER = 73, 103 | OTG_HS_EP1_OUT = 74, 104 | OTG_HS_EP1_IN = 75, 105 | OTG_HS_WKUP = 76, 106 | OTG_HS = 77, 107 | DCMI = 78, 108 | CRYP = 79, 109 | HASH_RNG = 80, 110 | FPU = 81, 111 | } 112 | 113 | pub fn init(nvic: &NvicInit) { 114 | unsafe { 115 | if nvic.enable { 116 | let mut tmppriority = (0x700 - (AIRCR.get() & 0x700)) >> 0x08; 117 | let tmppre = 0x4 - tmppriority; 118 | let tmpsub = 0x0F >> tmppriority; 119 | 120 | tmppriority = u32::from(nvic.priority) << tmppre; 121 | tmppriority |= u32::from(nvic.subpriority) & tmpsub; 122 | tmppriority <<= 0x04; 123 | 124 | IPR[nvic.irq_channel as usize].set(tmppriority); 125 | ISER[nvic.irq_channel as usize >> 5].set(0x1 << (nvic.irq_channel as u8 & 0x1F)); 126 | } else { 127 | ICER[nvic.irq_channel as usize >> 5].set(0x1 << (nvic.irq_channel as u8 & 0x1F)); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /breactor/src/mutex.rs: -------------------------------------------------------------------------------- 1 | //! Mutual exclusion for futures. 2 | 3 | use core::pin::Pin; 4 | use core::sync::atomic::{AtomicU32, Ordering}; 5 | use futures::task::Context; 6 | use futures::{Future, Poll}; 7 | 8 | use super::REACTOR; 9 | 10 | /// Mutex guarantees exclusive access for a task. 11 | /// 12 | /// Unlike pthread mutexes, this one is allowed to be released from a 13 | /// different thread of execution. 14 | /// 15 | /// This mutex is NOT recursive. The same task can not acquire it 16 | /// without releasing the previous lock. 17 | /// 18 | /// This mutex is lock-free. 19 | /// 20 | /// ## Access tokens 21 | /// The primary use case is issuing access tokens to shared resources 22 | /// (e.g., buses). 23 | /// 24 | /// ``` 25 | /// # #![feature(const_fn)] 26 | /// # #![feature(conservative_impl_trait)] 27 | /// # extern crate breactor; 28 | /// # extern crate futures; 29 | /// # use futures::{Future, FutureExt}; 30 | /// # use breactor::mutex::*; 31 | /// static BUS: Bus = Bus { mutex: Mutex::new() }; 32 | /// 33 | /// pub struct Bus { 34 | /// mutex: Mutex, 35 | /// } 36 | /// 37 | /// pub struct AccessToken { 38 | /// lock: MutexLock<'static>, 39 | /// } 40 | /// 41 | /// impl Bus { 42 | /// pub fn access(&'static self) -> impl Future { 43 | /// self.mutex.lock().map(|lock| AccessToken { lock }) 44 | /// } 45 | /// } 46 | /// 47 | /// impl AccessToken { 48 | /// pub fn exclusive_operation(&mut self) { 49 | /// println!("This operation is exclusive"); 50 | /// } 51 | /// } 52 | /// 53 | /// # fn main() { 54 | /// # } 55 | /// ``` 56 | #[allow(missing_debug_implementations)] 57 | pub struct Mutex { 58 | /// The tasks, that are currently waiting on the mutex. 59 | /// 60 | /// When the mutex is released, all those tasks are woken up. This 61 | /// usually results in the highest priority task acquiring a lock. 62 | wait_task_mask: AtomicU32, 63 | 64 | /// The current owner of the mutex lock. 65 | /// 66 | /// When 0, the mutex is empty. 67 | owner: AtomicU32, 68 | } 69 | 70 | /// If you have this lock, you have locked the underlying mutex. 71 | #[allow(missing_debug_implementations)] 72 | pub struct MutexLock<'a> { 73 | mutex: &'a Mutex, 74 | } 75 | 76 | #[allow(missing_debug_implementations)] 77 | pub struct LockFuture<'a> { 78 | mutex: &'a Mutex, 79 | } 80 | 81 | impl<'a> Drop for MutexLock<'a> { 82 | fn drop(&mut self) { 83 | self.mutex.release() 84 | } 85 | } 86 | 87 | impl Mutex { 88 | /// Creates new empty mutex. 89 | pub const fn new() -> Mutex { 90 | Mutex { 91 | wait_task_mask: AtomicU32::new(0), 92 | owner: AtomicU32::new(0), 93 | } 94 | } 95 | 96 | /// Return a future that will eventually lock the given mutex. 97 | pub const fn lock(&self) -> LockFuture { 98 | LockFuture { mutex: self } 99 | } 100 | 101 | /// Release the mutex, notifying all waiting tasks. 102 | fn release(&self) { 103 | self.owner.store(0, Ordering::SeqCst); 104 | let tasks = self.wait_task_mask.swap(0, Ordering::SeqCst); 105 | REACTOR.set_ready_task_mask(tasks); 106 | } 107 | } 108 | 109 | impl<'a> Future for LockFuture<'a> { 110 | type Output = MutexLock<'a>; 111 | 112 | fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll { 113 | // TODO(rasen): use waker 114 | let task = REACTOR.get_current_task_mask(); 115 | 116 | self.mutex.wait_task_mask.fetch_or(task, Ordering::SeqCst); 117 | 118 | let prev = self.mutex.owner.compare_and_swap(0, task, Ordering::SeqCst); 119 | if prev == 0 { 120 | // Mutex locked 121 | Poll::Ready(MutexLock { mutex: self.mutex }) 122 | } else { 123 | Poll::Pending 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/rasendubi/bkernel.svg?branch=master)](https://travis-ci.org/rasendubi/bkernel) 2 | 3 | bkernel is an experimental kernel for embedded devices written in Rust. I'm mostly trying out Rust now to see how it applies to kernel development. 4 | 5 | # Prerequisites 6 | 7 | ### Note for Nix users 8 | 9 | There is `shell.nix` for you. Just drop in with `nix-shell` and all dependencies are there (including nightly rust). 10 | 11 | Note: it won't work for platforms other than x86-64_linux. You should change rust-nightly hash in `shell.nix`. 12 | 13 | ## gcc-arm-none-eabi toolchain 14 | 15 | You need an gcc-arm-none-eabi toolchain before you can build the kernel. 16 | 17 | If you don't know where to get one, you can get it [there](https://launchpad.net/gcc-arm-embedded/+download): 18 | 19 | - Download one for your platform 20 | - Unpack 21 | - Add `/bin` to your `$PATH` variable 22 | 23 | ## Rust version 24 | 25 | This project needs lots of nightly features: 26 | 27 | - asm 28 | - core intrinsics 29 | - const fn 30 | - lang items 31 | - allocator 32 | - conservative impl trait 33 | - integer atomics 34 | - fixed size array 35 | 36 | Nightly builds are not backward-compatible, so only the latest version is supported (it changes every 6 weeks). That's why you need a reasonably up-to-date nightly rust. 37 | 38 | ## Rust sources 39 | 40 | bkernel needs Rust sources to build libcore for the target. If you don't have one, don't worry: it will be automatically downloaded to `rust` directory. 41 | 42 | If you have Rust git repo on you computer, you can point to it with: 43 | 44 | ```sh 45 | export RUSTDIR=/path/to/rust 46 | ``` 47 | 48 | Note: building bkernel will checkout rust to a commit your rustc was compiled with. 49 | 50 | # Build instructions 51 | 52 | Just invoke `make`. 53 | 54 | ## Make targets 55 | 56 | - `make` build all binaries, documentation and run tests; 57 | - `make build` only build kernel; 58 | - `make test` run tests; 59 | - `make doc` build documentation; 60 | - `make flash` flash kernel; 61 | - `make reset` reset the device; 62 | - `make device_test` run device tests. `$DEVICE` can be set to point to the device tty (defaults to `/dev/ttyUSB0`). 63 | 64 | # Running 65 | 66 | After booting the kernel you should see all LEDs are turned on and a terminal is running on PB6/PB7 pins. 67 | 68 | The following commands are supported: 69 | - `hi` - says hello 70 | - `+3`/`-3`/`+4`/`-4`/`+5`/`-5`/`+6`/`-6` - turn on/off LD3/4/5/6 71 | - `temp` - read temperature and humidity from HTU21D sensor 72 | - `panic` - throw a panic 73 | - `help` - for more commands 74 | 75 | # Device tests 76 | 77 | There are device tests that are executed with [expect](https://en.wikipedia.org/wiki/Expect). It must be installed on your system. 78 | 79 | You must flash the device before testing. 80 | 81 | To run device tests, execute: 82 | 83 | ```sh 84 | make device_test 85 | ``` 86 | 87 | or 88 | 89 | ```sh 90 | make device_test DEVICE=/dev/ttyUSB0 91 | ``` 92 | 93 | Note: device path can be different on your platform. 94 | 95 | # Issues 96 | 97 | If you have any issues or questions with the bkernel, just [open an issue](https://github.com/rasendubi/bkernel/issues) or mail me at [rasen.dubi@gmail.com](mailto:rasen.dubi@gmail.com). 98 | 99 | # License 100 | 101 | The bkernel source code is licensed by a modified GNU General Public License - the modification taking a form of an exception. The exception permits the source code of applications that use bkernel and are distributed as executables to remain closed source, thus permitting the use of bkernel in commercial applications without necessitating that the whole application be open sourced. The exception can only be used if you wish to combine bkernel with a proprietary product, and you comply with the terms stated in the exception itself. 102 | 103 | The full text of the bkernel license is available [here](LICENSE). 104 | -------------------------------------------------------------------------------- /dev/circular_buffer.rs: -------------------------------------------------------------------------------- 1 | //! Lock-free ring buffer. 2 | use ::core::array::FixedSizeArray; 3 | use ::core::cell::UnsafeCell; 4 | use ::core::marker::PhantomData; 5 | use ::core::sync::atomic::{AtomicUsize, Ordering}; 6 | 7 | /// A lock-free _single-producer_, _single-consumer_ buffer. 8 | /// 9 | /// Do *NOT* use it with either multiple producers or multiple 10 | /// consumers. 11 | #[allow(missing_debug_implementations)] 12 | pub struct CircularBuffer { 13 | array: UnsafeCell, 14 | tail: AtomicUsize, 15 | head: AtomicUsize, 16 | __phantom: PhantomData, 17 | } 18 | 19 | impl> CircularBuffer { 20 | /// Construct a new CircularBuffer initializing all elements to 21 | /// `init`. 22 | /// 23 | /// Note that you can't access these values and it is there merely 24 | /// to make this function `const`. 25 | /// 26 | /// ::core::mem::uninitialized would work here, but it is not 27 | /// const. (Have no idea why.) 28 | pub const fn new(init: A) -> CircularBuffer { 29 | CircularBuffer { 30 | array: UnsafeCell::new(init), 31 | tail: AtomicUsize::new(0), 32 | head: AtomicUsize::new(0), 33 | __phantom: PhantomData, 34 | } 35 | } 36 | 37 | fn increment(&self, idx: usize) -> usize { 38 | unsafe { (idx + 1) % (*self.array.get()).as_slice().len() } 39 | } 40 | 41 | /// Push an item into the buffer. 42 | /// 43 | /// Returns `true` if push was successful. 44 | /// `false` means the buffer was full. 45 | pub fn push(&self, item: T) -> bool { 46 | let current_tail = self.tail.load(Ordering::Relaxed); 47 | let next_tail = self.increment(current_tail); 48 | if next_tail == self.head.load(Ordering::Acquire) { 49 | // Queue is full 50 | false 51 | } else { 52 | unsafe { 53 | (*self.array.get()).as_mut_slice()[current_tail] = item; 54 | } 55 | self.tail.store(next_tail, Ordering::Release); 56 | 57 | true 58 | } 59 | } 60 | 61 | /// Pops element from the buffer. 62 | /// 63 | /// `None` means the buffer was empty. 64 | pub fn pop(&self) -> Option { 65 | let current_head = self.head.load(Ordering::Relaxed); 66 | if current_head == self.tail.load(Ordering::Acquire) { 67 | None 68 | } else { 69 | let item = unsafe { &mut *self.array.get() }.as_slice()[current_head].clone(); 70 | self.head 71 | .store(self.increment(current_head), Ordering::Release); 72 | 73 | Some(item) 74 | } 75 | } 76 | 77 | /// If the buffer was empty at the time of querying. 78 | /// 79 | /// Note that the status may have already changed by the time the 80 | /// function returns. 81 | pub fn was_empty(&self) -> bool { 82 | self.head.load(Ordering::Relaxed) == self.tail.load(Ordering::Relaxed) 83 | } 84 | 85 | /// Whether the buffer was full at the time of querying. 86 | /// 87 | /// Note that the status may have already changed by the time the 88 | /// function returns. 89 | pub fn was_full(&self) -> bool { 90 | let current_tail = self.tail.load(Ordering::Relaxed); 91 | let current_head = self.head.load(Ordering::Relaxed); 92 | 93 | self.increment(current_tail) == current_head 94 | } 95 | } 96 | 97 | unsafe impl> Sync for CircularBuffer {} 98 | 99 | #[cfg(test)] 100 | mod test { 101 | use super::*; 102 | 103 | #[test] 104 | fn test_init_is_empty() { 105 | let cb = CircularBuffer::new([0; 32]); 106 | assert_eq!(None, cb.pop()); 107 | } 108 | 109 | #[test] 110 | fn test_push_pop() { 111 | let cb = CircularBuffer::new([0; 32]); 112 | assert_eq!(true, cb.push(5)); 113 | assert_eq!(Some(5), cb.pop()); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /isr_vector.ld: -------------------------------------------------------------------------------- 1 | ENTRY(__isr_reset) 2 | 3 | PROVIDE(__isr_nmi = __isr_default); 4 | PROVIDE(__isr_hardfault = __isr_default); 5 | PROVIDE(__isr_memmanage = __isr_default); 6 | PROVIDE(__isr_busfault = __isr_default); 7 | PROVIDE(__isr_usagefault = __isr_default); 8 | PROVIDE(__isr_svc = __isr_default); 9 | PROVIDE(__isr_debugmon = __isr_default); 10 | PROVIDE(__isr_pendsv = __isr_default); 11 | PROVIDE(__isr_systick = __isr_default); 12 | PROVIDE(__isr_wwdg = __isr_default); 13 | PROVIDE(__isr_pvd = __isr_default); 14 | PROVIDE(__isr_tamp_stamp = __isr_default); 15 | PROVIDE(__isr_rtc_wkup = __isr_default); 16 | PROVIDE(__isr_flash = __isr_default); 17 | PROVIDE(__isr_rcc = __isr_default); 18 | PROVIDE(__isr_exti0 = __isr_default); 19 | PROVIDE(__isr_exti1 = __isr_default); 20 | PROVIDE(__isr_exti2 = __isr_default); 21 | PROVIDE(__isr_exti3 = __isr_default); 22 | PROVIDE(__isr_exti4 = __isr_default); 23 | PROVIDE(__isr_dma1_stream0 = __isr_default); 24 | PROVIDE(__isr_dma1_stream1 = __isr_default); 25 | PROVIDE(__isr_dma1_stream2 = __isr_default); 26 | PROVIDE(__isr_dma1_stream3 = __isr_default); 27 | PROVIDE(__isr_dma1_stream4 = __isr_default); 28 | PROVIDE(__isr_dma1_stream5 = __isr_default); 29 | PROVIDE(__isr_dma1_stream6 = __isr_default); 30 | PROVIDE(__isr_adc = __isr_default); 31 | PROVIDE(__isr_can1_tx = __isr_default); 32 | PROVIDE(__isr_can1_rx0 = __isr_default); 33 | PROVIDE(__isr_can1_rx1 = __isr_default); 34 | PROVIDE(__isr_can1_sce = __isr_default); 35 | PROVIDE(__isr_exti9_5 = __isr_default); 36 | PROVIDE(__isr_tim1_brk_tim9 = __isr_default); 37 | PROVIDE(__isr_tim1_up_tim10 = __isr_default); 38 | PROVIDE(__isr_tim1_trg_com_tim11 = __isr_default); 39 | PROVIDE(__isr_tim1_cc = __isr_default); 40 | PROVIDE(__isr_tim2 = __isr_default); 41 | PROVIDE(__isr_tim3 = __isr_default); 42 | PROVIDE(__isr_tim4 = __isr_default); 43 | PROVIDE(__isr_i2c1_ev = __isr_default); 44 | PROVIDE(__isr_i2c1_er = __isr_default); 45 | PROVIDE(__isr_i2c2_ev = __isr_default); 46 | PROVIDE(__isr_i2c2_er = __isr_default); 47 | PROVIDE(__isr_spi1 = __isr_default); 48 | PROVIDE(__isr_spi2 = __isr_default); 49 | PROVIDE(__isr_usart1 = __isr_default); 50 | PROVIDE(__isr_usart2 = __isr_default); 51 | PROVIDE(__isr_usart3 = __isr_default); 52 | PROVIDE(__isr_exti15_10 = __isr_default); 53 | PROVIDE(__isr_rtc_alarm = __isr_default); 54 | PROVIDE(__isr_otg_fs_wkup = __isr_default); 55 | PROVIDE(__isr_tim8_brk_tim12 = __isr_default); 56 | PROVIDE(__isr_tim8_up_tim13 = __isr_default); 57 | PROVIDE(__isr_tim8_trg_com_tim14 = __isr_default); 58 | PROVIDE(__isr_tim8_cc = __isr_default); 59 | PROVIDE(__isr_dma1_stream7 = __isr_default); 60 | PROVIDE(__isr_fsmc = __isr_default); 61 | PROVIDE(__isr_sdio = __isr_default); 62 | PROVIDE(__isr_tim5 = __isr_default); 63 | PROVIDE(__isr_spi3 = __isr_default); 64 | PROVIDE(__isr_uart4 = __isr_default); 65 | PROVIDE(__isr_uart5 = __isr_default); 66 | PROVIDE(__isr_tim6_dac = __isr_default); 67 | PROVIDE(__isr_tim7 = __isr_default); 68 | PROVIDE(__isr_dma2_stream0 = __isr_default); 69 | PROVIDE(__isr_dma2_stream1 = __isr_default); 70 | PROVIDE(__isr_dma2_stream2 = __isr_default); 71 | PROVIDE(__isr_dma2_stream3 = __isr_default); 72 | PROVIDE(__isr_dma2_stream4 = __isr_default); 73 | PROVIDE(__isr_eth = __isr_default); 74 | PROVIDE(__isr_eth_wkup = __isr_default); 75 | PROVIDE(__isr_can2_tx = __isr_default); 76 | PROVIDE(__isr_can2_rx0 = __isr_default); 77 | PROVIDE(__isr_can2_rx1 = __isr_default); 78 | PROVIDE(__isr_can2_sce = __isr_default); 79 | PROVIDE(__isr_otg_fs = __isr_default); 80 | PROVIDE(__isr_dma2_stream5 = __isr_default); 81 | PROVIDE(__isr_dma2_stream6 = __isr_default); 82 | PROVIDE(__isr_dma2_stream7 = __isr_default); 83 | PROVIDE(__isr_usart6 = __isr_default); 84 | PROVIDE(__isr_i2c3_ev = __isr_default); 85 | PROVIDE(__isr_i2c3_er = __isr_default); 86 | PROVIDE(__isr_otg_hs_ep1_out = __isr_default); 87 | PROVIDE(__isr_otg_hs_ep1_in = __isr_default); 88 | PROVIDE(__isr_otg_hs_wkup = __isr_default); 89 | PROVIDE(__isr_otg_hs = __isr_default); 90 | PROVIDE(__isr_dcmi = __isr_default); 91 | PROVIDE(__isr_cryp = __isr_default); 92 | PROVIDE(__isr_hash_rng = __isr_default); 93 | PROVIDE(__isr_fpu = __isr_default); 94 | -------------------------------------------------------------------------------- /breactor/src/promise.rs: -------------------------------------------------------------------------------- 1 | //! Lock-free synchronization point for single-value single-producer, 2 | //! single-consumer. 3 | use core::cell::UnsafeCell; 4 | use core::pin::Pin; 5 | use core::sync::atomic::{AtomicU32, Ordering}; 6 | 7 | use futures::task::Context; 8 | use futures::{Future, Poll}; 9 | 10 | use super::REACTOR; 11 | 12 | /// Promise provides a lock-free synchronization point for producer 13 | /// and consumer of the data, with Future-aware interface. 14 | /// 15 | /// The promise can be shared between one producer and one consumer. 16 | /// 17 | /// The consumer is assumed to hold the object and should not drop it 18 | /// until it is resolved. 19 | #[allow(missing_debug_implementations)] 20 | pub struct Promise { 21 | /// Stores the mask of the owning task. 22 | /// 23 | /// If `task` is `0`, the Promise have been resolved. 24 | task: AtomicU32, 25 | 26 | /// Stores the result of Promise. 27 | /// 28 | /// When `task` is non-zero, result stores `None`, and should only 29 | /// be written by the producer. 30 | /// 31 | /// When `task` is zero, the result stores `Some`, and should only 32 | /// be read by the consumer. 33 | result: UnsafeCell>, 34 | } 35 | 36 | unsafe impl Sync for Promise {} 37 | 38 | impl Promise { 39 | /// Creates an empty Promise. 40 | /// 41 | /// The promise must be claimed with `claim()` before calling 42 | /// `poll()` or `resolve()`. 43 | pub const unsafe fn empty() -> Promise { 44 | Promise { 45 | task: AtomicU32::new(0), 46 | result: UnsafeCell::new(None), 47 | } 48 | } 49 | 50 | /// Creates new promise and makes it be owned by the current task. 51 | /// 52 | /// Should only be called from within a task. 53 | pub fn new() -> Promise { 54 | Promise { 55 | task: AtomicU32::new(REACTOR.get_current_task_mask()), 56 | result: UnsafeCell::new(None), 57 | } 58 | } 59 | 60 | pub const fn new_task(task_mask: u32) -> Promise { 61 | Promise { 62 | task: AtomicU32::new(task_mask), 63 | result: UnsafeCell::new(None), 64 | } 65 | } 66 | 67 | /// Set the currently executed task as an owner. 68 | /// 69 | /// Should only be called from within a task. 70 | pub fn claim(&self) { 71 | let task = REACTOR.get_current_task_mask(); 72 | self.task.store(task, Ordering::Relaxed); 73 | } 74 | 75 | /// Resolves the Promise notifying the waiting task. 76 | /// 77 | /// This should be called by the producer. The producer is not 78 | /// allowed to use the object after calling `resolve()`. 79 | // TODO(rasen): create additional struct for producer's end, 80 | // which will consume on resolve? 81 | // 82 | // The Promise can track this end, which could allow dropping 83 | // promise before resolve. 84 | // 85 | // Also, I should consider making Promise be owned by the 86 | // producer and tracking consumer's future-part. 87 | pub fn resolve(&self, result: T) { 88 | unsafe { 89 | *self.result.get() = Some(result); 90 | } 91 | 92 | let task = self.task.swap(0, Ordering::Release); 93 | debug_assert_ne!(task, 0); 94 | REACTOR.set_ready_task_mask(task); 95 | } 96 | 97 | /// Returns true, if the promise is already resolved or not 98 | /// initialized. 99 | /// 100 | /// This method is not thread-safe with respect to `resolve()`. 101 | pub fn is_resolved(&self) -> bool { 102 | self.task.load(Ordering::Acquire) == 0 103 | } 104 | } 105 | 106 | impl Future for Promise { 107 | type Output = T; 108 | 109 | fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll { 110 | // TODO(rasen): use waker 111 | let task = self.task.load(Ordering::Acquire); 112 | if task == 0 { 113 | Poll::Ready(unsafe { ::core::ptr::replace(self.result.get(), None) }.unwrap()) 114 | } else { 115 | Poll::Pending 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /stm32f4/lib.rs: -------------------------------------------------------------------------------- 1 | //! STM32F4xx drivers. 2 | #![feature(lang_items)] 3 | #![feature(core_intrinsics)] 4 | #![feature(asm)] 5 | #![no_std] 6 | 7 | pub mod isr_vector; 8 | 9 | #[macro_use] 10 | pub mod volatile; 11 | pub mod crc; 12 | pub mod gpio; 13 | pub mod i2c; 14 | pub mod nvic; 15 | pub mod rcc; 16 | pub mod rng; 17 | pub mod timer; 18 | pub mod usart; 19 | 20 | pub mod lang_items; 21 | 22 | #[inline(always)] 23 | #[cfg(not(target_arch = "arm"))] 24 | pub unsafe fn __wait_for_interrupt() { 25 | panic!("__wait_for_interrupt is not implemented"); 26 | } 27 | 28 | #[inline(always)] 29 | #[cfg(target_arch = "arm")] 30 | pub unsafe fn __wait_for_interrupt() { 31 | asm!("wfi" : : : : "volatile"); 32 | } 33 | 34 | #[inline(always)] 35 | #[cfg(not(target_arch = "arm"))] 36 | pub unsafe fn __wait_for_event() { 37 | panic!("__wait_for_event is not implemented"); 38 | } 39 | 40 | #[inline(always)] 41 | #[cfg(target_arch = "arm")] 42 | pub unsafe fn __wait_for_event() { 43 | asm!("wfe" : : : : "volatile"); 44 | } 45 | 46 | #[inline(always)] 47 | #[cfg(not(target_arch = "arm"))] 48 | pub unsafe fn __set_event() { 49 | panic!("__set_event is not implemented"); 50 | } 51 | 52 | #[inline(always)] 53 | #[cfg(target_arch = "arm")] 54 | pub unsafe fn __set_event() { 55 | asm!("sev" : : : : "volatile"); 56 | } 57 | 58 | #[inline(always)] 59 | #[cfg(not(target_arch = "arm"))] 60 | pub unsafe fn __enable_irq() { 61 | panic!("enable_irq is not implemented"); 62 | } 63 | 64 | #[inline(always)] 65 | #[cfg(not(target_arch = "arm"))] 66 | pub unsafe fn __disable_irq() { 67 | panic!("disable_irq is not implemented"); 68 | } 69 | 70 | /// Get priority mask. 71 | #[inline(always)] 72 | #[cfg(not(target_arch = "arm"))] 73 | pub unsafe fn __get_primask() -> u32 { 74 | panic!("get_primask is not implemented"); 75 | } 76 | 77 | #[inline(always)] 78 | #[cfg(target_arch = "arm")] 79 | pub unsafe fn __enable_irq() { 80 | asm!("cpsie i" : : : : "volatile"); 81 | } 82 | 83 | #[inline(always)] 84 | #[cfg(target_arch = "arm")] 85 | pub unsafe fn __disable_irq() { 86 | asm!("cpsid i" : : : : "volatile"); 87 | } 88 | 89 | #[inline(always)] 90 | #[cfg(target_arch = "arm")] 91 | pub unsafe fn __get_primask() -> u32 { 92 | let result: u32; 93 | asm!("MRS $0, primask" : "=r" (result) : : : "volatile"); 94 | result 95 | } 96 | 97 | /// Saves current irq status and disables interrupts. 98 | /// Interrupts should always be restored with `restore_irq()`. 99 | /// 100 | /// # Examples 101 | /// 102 | /// ```no_run 103 | /// # use stm32f4::{save_irq, restore_irq}; 104 | /// # unsafe { 105 | /// let irq = save_irq(); 106 | /// // Do work with interrupts disabled 107 | /// restore_irq(irq); 108 | /// # } 109 | /// ``` 110 | #[inline(always)] 111 | pub unsafe fn save_irq() -> u32 { 112 | let primask = __get_primask(); 113 | __disable_irq(); 114 | primask 115 | } 116 | 117 | /// Enables interrupts if primask is non-zero. 118 | /// 119 | /// Must be used in pair with `save_irq()`. 120 | #[inline(always)] 121 | pub unsafe fn restore_irq(primask: u32) { 122 | if primask == 0 { 123 | __enable_irq(); 124 | } 125 | } 126 | 127 | /// A convenience wrapper around `save_irq` and `restore_irq`. 128 | #[derive(Debug)] 129 | pub struct IrqLock(u32); 130 | 131 | impl IrqLock { 132 | pub unsafe fn new() -> IrqLock { 133 | IrqLock(save_irq()) 134 | } 135 | } 136 | 137 | impl Drop for IrqLock { 138 | fn drop(&mut self) { 139 | unsafe { 140 | restore_irq(self.0); 141 | } 142 | } 143 | } 144 | 145 | /// Returns the unique device identifier. 146 | /// 147 | /// The 96-bit unique device identifier provides a reference number 148 | /// which is unique for any device and in any context. 149 | pub fn get_device_id() -> u128 { 150 | const REG: *const u32 = 0x1FFF_7A10 as _; 151 | unsafe { 152 | u128::from(*REG) | (u128::from(*REG.offset(1)) << 32) | (u128::from(*REG.offset(2)) << 64) 153 | } 154 | } 155 | 156 | /// Returns the flash memory size in kbytes. 157 | pub fn get_flash_size() -> u16 { 158 | const REG: *const u16 = 0x1FFF_7A22 as _; 159 | unsafe { *REG } 160 | } 161 | -------------------------------------------------------------------------------- /stm32f4/gpio.rs: -------------------------------------------------------------------------------- 1 | //! General-Purpose Input/Output driver 2 | 3 | #![allow(non_camel_case_types)] 4 | 5 | use crate::volatile::RW; 6 | 7 | extern "C" { 8 | pub static GPIO_A: Gpio; 9 | pub static GPIO_B: Gpio; 10 | pub static GPIO_C: Gpio; 11 | pub static GPIO_D: Gpio; 12 | pub static GPIO_E: Gpio; 13 | pub static GPIO_F: Gpio; 14 | pub static GPIO_G: Gpio; 15 | pub static GPIO_H: Gpio; 16 | pub static GPIO_I: Gpio; 17 | pub static GPIO_J: Gpio; 18 | pub static GPIO_K: Gpio; 19 | } 20 | 21 | #[repr(C)] 22 | #[allow(missing_debug_implementations)] 23 | pub struct Gpio { 24 | moder: RW, // 0x0 25 | otyper: RW, // 0x4 26 | ospeedr: RW, // 0x8 27 | pupdr: RW, // 0xC 28 | idr: RW, // 0x10 29 | odr: RW, // 0x14 30 | bsrr: RW, // 0x18 31 | lckr: RW, // 0x1C 32 | afrl: RW, // 0x20 33 | afrh: RW, // 0x24 34 | } 35 | 36 | #[test] 37 | fn test_register_size() { 38 | assert_eq!(0x28, ::core::mem::size_of::()); 39 | } 40 | 41 | #[derive(Debug)] 42 | pub struct GpioConfig { 43 | pub mode: GpioMode, 44 | pub otype: GpioOType, 45 | pub ospeed: GpioOSpeed, 46 | pub pupd: GpioPuPd, 47 | pub af: GpioAF, 48 | } 49 | 50 | #[derive(Copy, Clone, Debug)] 51 | #[repr(u32)] 52 | pub enum GpioMode { 53 | INPUT = 0x0, 54 | OUTPUT = 0x1, 55 | AF = 0x2, 56 | ANALOG = 0x3, 57 | } 58 | 59 | #[derive(Copy, Clone, Debug)] 60 | #[repr(u32)] 61 | pub enum GpioOType { 62 | PUSH_PULL = 0x0, 63 | OPEN_DRAIN = 0x1, 64 | } 65 | 66 | #[derive(Copy, Clone, Debug)] 67 | #[repr(u32)] 68 | pub enum GpioOSpeed { 69 | LOW_SPEED = 0x0, 70 | MEDIUM_SPEED = 0x1, 71 | FAST_SPEED = 0x2, 72 | HIGH_SPEED = 0x3, 73 | } 74 | 75 | #[derive(Copy, Clone, Debug)] 76 | #[repr(u32)] 77 | pub enum GpioPuPd { 78 | NO = 0x0, 79 | PULL_UP = 0x1, 80 | PULL_DOWN = 0x2, 81 | } 82 | 83 | /// Alternate Function 84 | #[derive(Copy, Clone, Debug)] 85 | #[repr(u32)] 86 | pub enum GpioAF { 87 | AF0 = 0x0, 88 | AF1 = 0x1, 89 | AF2 = 0x2, 90 | AF3 = 0x3, 91 | AF4 = 0x4, 92 | AF5 = 0x5, 93 | AF6 = 0x6, 94 | AF7 = 0x7, 95 | AF8 = 0x8, 96 | AF9 = 0x9, 97 | AF10 = 0xA, 98 | AF11 = 0xB, 99 | AF12 = 0xC, 100 | AF13 = 0xD, 101 | AF14 = 0xE, 102 | AF15 = 0xF, 103 | } 104 | 105 | impl Gpio { 106 | /// Enables a given pin on GPIO. Pins are numbered starting from 0. 107 | /// 108 | /// # Examples 109 | /// 110 | /// Enable PB6 with Alternate Function 7 (USART1), fast speed, open-drain. 111 | /// 112 | /// ```no_run 113 | /// use stm32f4::gpio; 114 | /// 115 | /// unsafe { 116 | /// gpio::GPIO_B.enable(6, gpio::GpioConfig { 117 | /// mode: gpio::GpioMode::AF, 118 | /// ospeed: gpio::GpioOSpeed::FAST_SPEED, 119 | /// otype: gpio::GpioOType::OPEN_DRAIN, 120 | /// pupd: gpio::GpioPuPd::PULL_UP, 121 | /// af: gpio::GpioAF::AF7, 122 | /// }); 123 | /// } 124 | /// ``` 125 | pub fn enable(&self, pin: u32, config: GpioConfig) { 126 | unsafe { 127 | self.ospeedr 128 | .update_with_mask(0x3 << (pin * 2), (config.ospeed as u32) << (pin * 2)); 129 | self.otyper 130 | .update_with_mask(0x1 << pin, (config.otype as u32) << pin); 131 | self.pupdr 132 | .update_with_mask(0x3 << (pin * 2), (config.pupd as u32) << (pin * 2)); 133 | 134 | if pin < 8 { 135 | self.afrl 136 | .update_with_mask(0xf << (pin * 4), (config.af as u32) << (pin * 4)); 137 | } else { 138 | self.afrh.update_with_mask( 139 | 0xf << ((pin - 8) * 4), 140 | (config.af as u32) << ((pin - 8) * 4), 141 | ); 142 | } 143 | self.moder 144 | .update_with_mask(0x3 << (pin * 2), (config.mode as u32) << (pin * 2)); 145 | } 146 | } 147 | 148 | pub fn set_bit(&self, pin: u32) { 149 | unsafe { 150 | self.bsrr.set(0x1 << pin); 151 | } 152 | } 153 | 154 | pub fn clear_bit(&self, pin: u32) { 155 | unsafe { 156 | self.bsrr.set(0x1 << (pin + 16)); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /stm32f4/timer.rs: -------------------------------------------------------------------------------- 1 | //! General-purpose timers (TIM2-TIM5) 2 | 3 | // allow `<< 0` 4 | #![allow(clippy::identity_op)] 5 | 6 | use crate::volatile::{RES, RW}; 7 | 8 | extern "C" { 9 | pub static TIM2: Tim; 10 | pub static TIM3: Tim; 11 | pub static TIM4: Tim; 12 | pub static TIM5: Tim; 13 | } 14 | 15 | #[repr(C)] 16 | #[allow(missing_debug_implementations)] 17 | pub struct Tim { 18 | cr1: RW, // 0x00 19 | cr2: RW, // 0x04 20 | smcr: RW, // 0x08 21 | dier: RW, // 0x0C 22 | sr: RW, // 0x10 23 | egr: RW, // 0x14 24 | ccmr1: RW, // 0x18 25 | ccmr2: RW, // 0x1C 26 | ccer: RW, // 0x20 27 | cnt: RW, // 0x24 28 | psc: RW, // 0x28 29 | arr: RW, // 0x2c 30 | _0: RES, // 0x30 31 | ccr1: RW, // 0x34 32 | ccr2: RW, // 0x38 33 | ccr3: RW, // 0x3C 34 | ccr4: RW, // 0x40 35 | _1: RES, // 0x44 36 | dcr: RW, // 0x48 37 | dmar: RW, // 0x4C 38 | /// Unique to TIM2 and TIM5 39 | or: RW, // 0x50 40 | } 41 | 42 | #[test] 43 | fn test_register_size() { 44 | assert_eq!(0x54, ::core::mem::size_of::()); 45 | } 46 | 47 | #[allow(dead_code)] 48 | #[derive(Copy, Clone)] 49 | #[repr(u32)] 50 | enum Cr1 { 51 | CEN = 1 << 0, 52 | UDIS = 1 << 1, 53 | URS = 1 << 2, 54 | OPM = 1 << 3, 55 | DIR = 1 << 4, 56 | CMS = 3 << 5, 57 | ARPE = 1 << 7, 58 | CKD = 3 << 8, 59 | } 60 | 61 | #[derive(Copy, Clone, Debug)] 62 | #[repr(u32)] 63 | pub enum Dier { 64 | UIE = 1 << 0, 65 | CC1IE = 1 << 1, 66 | CC2IE = 1 << 2, 67 | CC3IE = 1 << 3, 68 | CC4IE = 1 << 4, 69 | TIE = 1 << 6, 70 | UDE = 1 << 8, 71 | CC1DE = 1 << 9, 72 | CC2DE = 1 << 10, 73 | CC3DE = 1 << 11, 74 | CC4DE = 1 << 12, 75 | TDE = 1 << 14, 76 | } 77 | 78 | #[allow(dead_code)] 79 | #[derive(Copy, Clone)] 80 | #[repr(u32)] 81 | enum Egr { 82 | UG = 1 << 0, 83 | CC1G = 1 << 1, 84 | CC2G = 1 << 2, 85 | CC3G = 1 << 3, 86 | CC4G = 1 << 4, 87 | TG = 1 << 6, 88 | } 89 | 90 | #[derive(Debug)] 91 | pub struct TimInit { 92 | pub prescaler: u16, 93 | pub counter_mode: CounterMode, 94 | pub period: u32, 95 | pub clock_division: ClockDivision, 96 | pub repetition_counter: u8, 97 | } 98 | 99 | #[derive(Copy, Clone, Debug)] 100 | #[repr(u32)] 101 | pub enum CounterMode { 102 | Up = 0x0000, 103 | Down = 0x0010, 104 | CenterAligned1 = 0x0020, 105 | CenterAligned2 = 0x0040, 106 | CenterAligned3 = 0x0060, 107 | } 108 | 109 | #[derive(Copy, Clone, Debug)] 110 | #[repr(u32)] 111 | pub enum ClockDivision { 112 | Div1 = 0x0000, 113 | Div2 = 0x0100, 114 | Div3 = 0x0200, 115 | } 116 | 117 | impl Tim { 118 | pub fn init(&self, tim: &TimInit) { 119 | unsafe { 120 | let mut tmpcr1 = self.cr1.get(); 121 | tmpcr1 &= !(Cr1::DIR as u32 | Cr1::CMS as u32); 122 | tmpcr1 |= tim.counter_mode as u32; 123 | self.cr1.set(tmpcr1); 124 | 125 | self.arr.set(tim.period); 126 | self.psc.set(u32::from(tim.prescaler)); 127 | self.egr.set(Egr::TG as u32); 128 | } 129 | } 130 | 131 | pub fn enable(&self) { 132 | unsafe { 133 | self.cr1.set_flag(Cr1::CEN as u32); 134 | } 135 | } 136 | 137 | pub fn disable(&self) { 138 | unsafe { 139 | self.cr1.clear_flag(Cr1::CEN as u32); 140 | } 141 | } 142 | 143 | pub fn get_counter(&self) -> u32 { 144 | unsafe { self.cnt.get() } 145 | } 146 | 147 | pub fn it_enable(&self, it: Dier) { 148 | unsafe { 149 | self.dier.set_flag(it as u32); 150 | } 151 | } 152 | 153 | pub fn it_disable(&self, it: Dier) { 154 | unsafe { 155 | self.dier.clear_flag(it as u32); 156 | } 157 | } 158 | 159 | pub fn it_status(&self, it: Dier) -> bool { 160 | unsafe { 161 | let itstatus = self.sr.get() & it as u32; 162 | let itenable = self.dier.get() & it as u32; 163 | itstatus != 0 && itenable != 0 164 | } 165 | } 166 | 167 | pub fn it_clear_pending(&self, it: Dier) { 168 | unsafe { 169 | self.sr.clear_flag(it as u32); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /stm32f4/rng.rs: -------------------------------------------------------------------------------- 1 | //! Random number generator. 2 | 3 | // allow `<< 0` 4 | #![allow(clippy::identity_op)] 5 | 6 | use crate::volatile::{RO, RW}; 7 | 8 | extern "C" { 9 | pub static RNG: Rng; 10 | } 11 | 12 | #[repr(C)] 13 | #[allow(missing_debug_implementations)] 14 | pub struct Rng { 15 | cr: RW, 16 | sr: RW, 17 | dr: RO, 18 | } 19 | 20 | #[derive(Copy, Clone)] 21 | #[repr(u32)] 22 | enum CrMask { 23 | /// Interrupt enable. 24 | /// 25 | /// 0: Interrupt is disabled. 26 | /// 1: Interrupt is enabled. An interrupt is pending soon as 27 | /// DRDY=1 or SEIS=1 or CEIS=1 in the SR register. 28 | IE = 0x1 << 3, 29 | 30 | /// Random number generator enable. 31 | /// 32 | /// 0: Random number generator is disabled. 33 | /// 1: Random number generator is enabled. 34 | RNDGEN = 0x1 << 2, 35 | } 36 | 37 | #[repr(u32)] 38 | #[derive(Debug)] 39 | pub enum SrMask { 40 | /// Seed error interrupt status. 41 | /// 42 | /// This bit is set at the same time as SECS, it is cleared by 43 | /// writing it to 0. 44 | /// 45 | /// 0: No faulty sequence detected. 46 | /// 1: One of the following faulty sequences has been detected: 47 | /// - More than 64 consecutive bits as the same value (0 or 1) 48 | /// - More than 32 consecutive alternances of 0 and 1 49 | /// (01010101...01) 50 | /// 51 | /// An interrupt is pending if IE = 1 in the CR register. 52 | SEIS = 0x1 << 6, 53 | 54 | /// Clock error interrupt status. 55 | /// 56 | /// The bit is set at the same time as CECS, it is cleared by 57 | /// writing it to 0. 58 | /// 59 | /// 0: The CLK clock was correctly detected. 60 | /// 1: The CLK was not correctly detected (f_clk < f_hclk/16) 61 | /// 62 | /// An interrupt is pending if IE = 1 in the CR register. 63 | CEIS = 0x1 << 5, 64 | 65 | /// Seed error current status. 66 | /// 67 | /// 0: No faulty sequence has currently been detected. If the SEIS 68 | /// bit is set, this means that a faulty sequence was detected and 69 | /// the situation has been recovered. 70 | /// 1: One of the following faulty sequences has been detected: 71 | /// - More than 64 consecutive bits at the same value (0 or 1) 72 | /// - More than 32 consecutive alternatives of 0 and 1 73 | /// (010101...01) 74 | SECS = 0x1 << 2, 75 | 76 | /// Clock error current status. 77 | /// 78 | /// 0: RNG_CLK clock has been correctly detected. If the CEIS bit 79 | /// is set, this means that a clock error was detected and the 80 | /// situation has been recovered. 81 | /// 1: RNG_CLK was not correctly detected (f_rng_clk < f_hclk/16). 82 | CECS = 0x1 << 1, 83 | 84 | /// Data ready. 85 | /// 86 | /// 0: The DR register is not yet valid, no random data is 87 | /// available. 88 | /// 1: The DR register contains valid random data. 89 | /// 90 | /// Note: An interrupt is pending if IE = 1 in the CR register. 91 | /// Once the DR register has been read, this bit returns to 0 92 | /// until a new valid value is computed. 93 | DRDY = 0x1 << 0, 94 | } 95 | 96 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 97 | pub enum Error { 98 | SeedError, 99 | ClockError, 100 | } 101 | 102 | impl Rng { 103 | pub fn enable(&self) { 104 | unsafe { 105 | self.cr.set_flag(CrMask::RNDGEN as u32); 106 | } 107 | } 108 | 109 | pub fn disable(&self) { 110 | unsafe { 111 | self.cr.clear_flag(CrMask::RNDGEN as u32); 112 | } 113 | } 114 | 115 | pub fn it_enable(&self) { 116 | unsafe { 117 | self.cr.set_flag(CrMask::IE as u32); 118 | } 119 | } 120 | 121 | pub fn it_disable(&self) { 122 | unsafe { 123 | self.cr.clear_flag(CrMask::IE as u32); 124 | } 125 | } 126 | 127 | pub fn sr_status(&self, mask: SrMask) -> bool { 128 | (unsafe { self.sr.get() }) & (mask as u32) != 0 129 | } 130 | 131 | pub fn get(&self) -> Result, Error> { 132 | let sr = unsafe { self.sr.get() }; 133 | if sr & (SrMask::SECS as u32) != 0 { 134 | // From reference manual (24.3.2): 135 | // 136 | // "In the case of a seed error, [...]. If a number is 137 | // available in the DR register, it must not be used 138 | // because it may not have enough entropy." 139 | Err(Error::SeedError) 140 | } else if sr & (SrMask::DRDY as u32) != 0 { 141 | Ok(Some(unsafe { self.dr.get() })) 142 | } else if sr & (SrMask::CECS as u32) != 0 { 143 | // From reference manual (24.3.2): 144 | // 145 | // "The clock error has no impact on the previously 146 | // generated random numbers, and the DR register contents 147 | // can be used." 148 | Err(Error::ClockError) 149 | } else { 150 | Ok(None) 151 | } 152 | } 153 | 154 | pub unsafe fn get_data_unchecked(&self) -> u32 { 155 | self.dr.get() 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /dev/usart.rs: -------------------------------------------------------------------------------- 1 | //! Future-based USART driver. 2 | 3 | use core::pin::Pin; 4 | use core::task::Context; 5 | use stm32f4::usart; 6 | 7 | use crate::circular_buffer::CircularBuffer; 8 | use crate::resettable_stream::ResettableStream; 9 | 10 | use futures::{Poll, Sink, Stream}; 11 | 12 | use core::array::FixedSizeArray; 13 | use core::sync::atomic::{AtomicU32, Ordering}; 14 | 15 | use breactor::REACTOR; 16 | 17 | #[allow(missing_debug_implementations)] 18 | pub struct Usart { 19 | usart: &'static usart::Usart, 20 | writer_task_mask: AtomicU32, 21 | reader_task_mask: AtomicU32, 22 | writer_buffer: CircularBuffer, 23 | reader_buffer: CircularBuffer, 24 | } 25 | 26 | impl, B: FixedSizeArray> Usart { 27 | pub const fn new( 28 | usart: &'static usart::Usart, 29 | writer_buffer: A, 30 | reader_buffer: B, 31 | ) -> Usart { 32 | Usart { 33 | usart, 34 | writer_task_mask: AtomicU32::new(0), 35 | reader_task_mask: AtomicU32::new(0), 36 | writer_buffer: CircularBuffer::new(writer_buffer), 37 | reader_buffer: CircularBuffer::new(reader_buffer), 38 | } 39 | } 40 | 41 | pub fn try_push_writer(&self, item: u8) -> bool { 42 | let res = self.writer_buffer.push(item); 43 | if res { 44 | self.writer_task_mask.store(0, Ordering::SeqCst); 45 | 46 | // This triggers TXE interrupt if transmitter is already 47 | // empty, so the USART catches up with new data. 48 | self.usart.it_enable(usart::Interrupt::TXE); 49 | } 50 | res 51 | } 52 | 53 | pub fn try_pop_writer(&self) -> Option { 54 | let res = self.writer_buffer.pop(); 55 | if res.is_some() { 56 | let task_mask = self.writer_task_mask.swap(0, Ordering::SeqCst); 57 | REACTOR.set_ready_task_mask(task_mask); 58 | } 59 | res 60 | } 61 | 62 | pub fn try_push_reader(&self, item: u8) -> bool { 63 | let res = self.reader_buffer.push(item); 64 | if res { 65 | let task_mask = self.reader_task_mask.swap(0, Ordering::SeqCst); 66 | REACTOR.set_ready_task_mask(task_mask); 67 | } 68 | res 69 | } 70 | 71 | pub fn try_pop_reader(&self) -> Option { 72 | self.reader_buffer.pop() 73 | } 74 | 75 | /// Interrupt service routine. 76 | /// 77 | /// It should be called for the corresponding USART interrupt. 78 | /// 79 | /// # Example 80 | /// ```no_run 81 | /// # #![feature(const_fn)] 82 | /// # extern crate dev; 83 | /// # extern crate stm32f4; 84 | /// # use dev::usart::Usart; 85 | /// static USART2: Usart<[u8; 32], [u8;32]> = Usart::new(unsafe {&stm32f4::usart::USART2}, [0; 32], [0;32]); 86 | /// 87 | /// pub unsafe extern fn __isr_usart2() { 88 | /// USART2.isr() 89 | /// } 90 | /// # pub fn main() { 91 | /// # } 92 | /// ``` 93 | pub unsafe fn isr(&self) { 94 | if self.usart.it_status(usart::Interrupt::RXNE) { 95 | let c = self.usart.get_unsafe(); 96 | // If the buffer is full, we discard _new_ input. 97 | // That's not ideal :( 98 | let _ = self.try_push_reader(c); 99 | } 100 | 101 | if self.usart.it_status(usart::Interrupt::TXE) { 102 | if let Some(c) = self.try_pop_writer() { 103 | self.usart.put_unsafe(c); 104 | } else { 105 | self.usart.it_disable(usart::Interrupt::TXE); 106 | } 107 | } 108 | } 109 | } 110 | 111 | impl<'a, A: FixedSizeArray, B: FixedSizeArray> Sink for &'a Usart { 112 | type SinkError = (); 113 | 114 | fn poll_ready( 115 | self: Pin<&mut Self>, 116 | _cx: &mut Context<'_>, 117 | ) -> Poll> { 118 | self.writer_task_mask 119 | .store(REACTOR.get_current_task_mask(), Ordering::SeqCst); 120 | 121 | if self.writer_buffer.was_full() { 122 | Poll::Pending 123 | } else { 124 | self.writer_task_mask.store(0, Ordering::SeqCst); 125 | 126 | Poll::Ready(Ok(())) 127 | } 128 | } 129 | 130 | fn start_send(self: Pin<&mut Self>, item: u8) -> Result<(), Self::SinkError> { 131 | if self.try_push_writer(item) { 132 | // This triggers TXE interrupt if transmitter was 133 | // empty, so the USART catches up with new data. 134 | self.usart.it_enable(usart::Interrupt::TXE); 135 | 136 | Ok(()) 137 | } else { 138 | panic!("Usart: start_send was called, but the queue is not ready"); 139 | } 140 | } 141 | 142 | fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { 143 | self.writer_task_mask 144 | .store(REACTOR.get_current_task_mask(), Ordering::SeqCst); 145 | 146 | if self.writer_buffer.was_empty() { 147 | self.writer_task_mask.store(0, Ordering::SeqCst); 148 | Poll::Ready(Ok(())) 149 | } else { 150 | Poll::Pending 151 | } 152 | } 153 | 154 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 155 | self.poll_flush(cx) 156 | } 157 | } 158 | 159 | impl<'a, A: FixedSizeArray, B: FixedSizeArray> Stream for &'a Usart { 160 | type Item = u8; 161 | 162 | fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { 163 | self.reader_task_mask 164 | .store(REACTOR.get_current_task_mask(), Ordering::SeqCst); 165 | 166 | match self.try_pop_reader() { 167 | Some(x) => { 168 | self.reader_task_mask.store(0, Ordering::SeqCst); 169 | Poll::Ready(Some(x)) 170 | } 171 | None => Poll::Pending, 172 | } 173 | } 174 | } 175 | 176 | impl<'a, A: FixedSizeArray, B: FixedSizeArray> ResettableStream for &'a Usart { 177 | fn reset(&mut self) { 178 | while let Some(_) = self.try_pop_reader() {} 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /stm32f4/volatile.rs: -------------------------------------------------------------------------------- 1 | //! Volatile wrappers. 2 | //! 3 | //! This module provides a wrapper around `volatile_load` and 4 | //! `volatile_store`, so user shouldn't use compiler intrinsics 5 | //! directly. 6 | 7 | use core::intrinsics::{volatile_load, volatile_store}; 8 | 9 | use core::fmt::{Debug, Error, Formatter}; 10 | 11 | use core::ops::{BitAnd, BitOr, Not}; 12 | 13 | /// Represents a volatile register. 14 | /// 15 | /// `Volatile` represents a volatile register of type `T`. 16 | /// It's analagous to C's: `volatile T *` type. 17 | pub struct Volatile(pub *mut T); 18 | 19 | impl Debug for Volatile { 20 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 21 | write!(f, "Volatile({0:p})", self.0) 22 | } 23 | } 24 | 25 | impl PartialEq for Volatile { 26 | fn eq(&self, other: &Volatile) -> bool { 27 | self.0 == other.0 28 | } 29 | } 30 | 31 | impl Volatile { 32 | /// Cast-constructor for `Volatile`. It creates a volatile 33 | /// variable implicitly casting from `usize`, so you don't have to 34 | /// cast yourself. 35 | /// 36 | /// # Example 37 | /// ``` 38 | /// # use stm32f4::volatile::Volatile; 39 | /// assert_eq!(Volatile(0x40020100 as *mut u32), Volatile::new(0x40020100)); 40 | /// ``` 41 | pub fn new(addr: usize) -> Volatile { 42 | Volatile(addr as *mut T) 43 | } 44 | 45 | /// Use instead of `volatile_store`. 46 | pub unsafe fn set(&self, value: T) { 47 | volatile_store(self.0, value) 48 | } 49 | 50 | /// Use instead of `volatile_load`. 51 | pub unsafe fn get(&self) -> T { 52 | volatile_load(self.0) 53 | } 54 | } 55 | 56 | /// Read-only register 57 | #[repr(C)] 58 | #[allow(missing_debug_implementations)] 59 | pub struct RO(T); 60 | 61 | impl RO { 62 | /// Volatile read 63 | pub unsafe fn get(&self) -> T { 64 | volatile_load(&self.0) 65 | } 66 | } 67 | 68 | /// Write-only register 69 | #[repr(C)] 70 | #[allow(missing_debug_implementations)] 71 | pub struct WO(T); 72 | 73 | impl WO { 74 | /// Volatile store 75 | pub unsafe fn set(&self, value: T) { 76 | volatile_store(&self.0 as *const T as *mut T, value) 77 | } 78 | } 79 | 80 | /// Read-write register 81 | #[repr(C)] 82 | #[allow(missing_debug_implementations)] 83 | pub struct RW(T); 84 | 85 | impl RW { 86 | /// Volatile read 87 | pub unsafe fn get(&self) -> T { 88 | volatile_load(&self.0) 89 | } 90 | 91 | /// Volatile store 92 | pub unsafe fn set(&self, value: T) { 93 | volatile_store(&self.0 as *const T as *mut T, value) 94 | } 95 | 96 | /// Updates value of a register 97 | /// 98 | /// # Examples 99 | /// ``` 100 | /// # use stm32f4::volatile::RW; 101 | /// # unsafe { 102 | /// let reg: RW = std::mem::uninitialized(); 103 | /// reg.set(0x2e); 104 | /// reg.update(|x| { 105 | /// assert_eq!(0x2e, x); 106 | /// 0x3f 107 | /// }); 108 | /// assert_eq!(0x3f, reg.get()); 109 | /// # } 110 | /// ``` 111 | pub unsafe fn update(&self, f: F) 112 | where 113 | F: FnOnce(T) -> T, 114 | { 115 | self.set(f(self.get())) 116 | } 117 | 118 | /// Performs read-modify-write and updates part of register under 119 | /// `mask`. 120 | /// 121 | /// # Examples 122 | /// ``` 123 | /// # use stm32f4::volatile::RW; 124 | /// # unsafe { 125 | /// let reg: RW = std::mem::uninitialized(); 126 | /// reg.set(0xdeadbabe); 127 | /// reg.update_with_mask(0xffff0000, 0xcafe0000); 128 | /// assert_eq!(0xcafebabe, reg.get()); 129 | /// # } 130 | /// ``` 131 | pub unsafe fn update_with_mask(&self, mask: T, value: T) 132 | where 133 | T: Not + BitAnd + BitOr, 134 | { 135 | self.update(|x| x & !mask | value); 136 | } 137 | 138 | /// Sets flag in the register. 139 | /// 140 | /// # Examples 141 | /// ``` 142 | /// # use stm32f4::volatile::RW; 143 | /// # unsafe { 144 | /// let reg: RW = std::mem::uninitialized(); 145 | /// reg.set(0x2e); 146 | /// reg.set_flag(0x11); 147 | /// assert_eq!(0x3f, reg.get()); 148 | /// # } 149 | /// ``` 150 | pub unsafe fn set_flag(&self, value: T) 151 | where 152 | T: BitOr, 153 | { 154 | self.update(|x| x | value); 155 | } 156 | 157 | /// Clears flag in the register. 158 | /// 159 | /// # Examples 160 | /// ``` 161 | /// # use stm32f4::volatile::RW; 162 | /// # unsafe { 163 | /// let reg: RW = std::mem::uninitialized(); 164 | /// reg.set(0x3f); 165 | /// reg.clear_flag(0x11); 166 | /// assert_eq!(0x2e, reg.get()); 167 | /// # } 168 | /// ``` 169 | pub unsafe fn clear_flag(&self, value: T) 170 | where 171 | T: Not + BitAnd, 172 | { 173 | self.update(|x| x & !value); 174 | } 175 | } 176 | 177 | /// Reserved register. 178 | /// 179 | /// There is no operations defined and the structure is hidden, so 180 | /// there is nothing you can do with reserved register - it's reserved 181 | /// after all. 182 | #[repr(C)] 183 | #[allow(missing_debug_implementations)] 184 | pub struct RES(T); 185 | 186 | /// Define a set of registers with a shorter syntax. 187 | /// 188 | /// # Examples 189 | /// ``` 190 | /// # #[macro_use] extern crate stm32f4; 191 | /// # use stm32f4::volatile::Volatile; 192 | /// # fn main() { 193 | /// const RCC_BASE: usize = 0x40023800; 194 | /// registers! { 195 | /// RCC_BASE, u32 => { 196 | /// RCC_CR = 0x00, 197 | /// RCC_PLLCFGR = 0x04, 198 | /// } 199 | /// } 200 | /// assert_eq!(Volatile(0x40023800 as *mut u32), RCC_CR); 201 | /// assert_eq!(Volatile(0x40023804 as *mut u32), RCC_PLLCFGR); 202 | /// # } 203 | /// ``` 204 | /// 205 | /// This also support explicit type for all registers: 206 | /// 207 | /// ``` 208 | /// # #[macro_use] extern crate stm32f4; 209 | /// # use stm32f4::volatile::Volatile; 210 | /// # fn main() { 211 | /// const USART1_BASE: usize = 0x40011000; 212 | /// registers! { 213 | /// USART1_BASE => { 214 | /// USART1_SR: u32 = 0x0, 215 | /// USART1_DR: u8 = 0x4 216 | /// } 217 | /// } 218 | /// assert_eq!(Volatile(0x40011000 as *mut u32), USART1_SR); 219 | /// assert_eq!(Volatile(0x40011004 as *mut u8), USART1_DR); 220 | /// # } 221 | /// ``` 222 | /// 223 | /// # Known bugs 224 | /// It's not possible to attach a doc to a register. 225 | /// 226 | /// The following doesn't compile: 227 | /// 228 | /// ```ignore 229 | /// # #[macro_use] extern crate kernel; 230 | /// # use stm32f4::volatile::Volatile; 231 | /// # fn main() { 232 | /// const USART1_BASE: usize = 0x40011000; 233 | /// registers! { 234 | /// USART1_BASE => { 235 | /// USART1_SR: u32 = 0x0, 236 | /// /// Data register 237 | /// USART1_DR: u8 = 0x4 238 | /// } 239 | /// } 240 | /// # } 241 | /// ``` 242 | #[macro_export] 243 | macro_rules! registers { 244 | ( $base:expr => { $($v:ident : $t:ty = $e:expr),* } ) => ( 245 | $( 246 | const $v: $crate::volatile::Volatile<$t> = $crate::volatile::Volatile(($base as usize + $e) as *mut $t); 247 | )* 248 | ); 249 | ( $base:expr => { $($v:ident : $t:ty = $e:expr),* , } ) => ( 250 | $( 251 | const $v: $crate::volatile::Volatile<$t> = $crate::volatile::Volatile(($base as usize + $e) as *mut $t); 252 | )* 253 | ); 254 | 255 | ( $base:expr , $t:ty => { $($v:ident = $e:expr),* } ) => ( 256 | $( 257 | const $v: $crate::volatile::Volatile<$t> = $crate::volatile::Volatile(($base as usize + $e) as *mut $t); 258 | )* 259 | ); 260 | ( $base:expr , $t:ty => { $($v:ident = $e:expr),* , } ) => ( 261 | $( 262 | const $v: $crate::volatile::Volatile<$t> = $crate::volatile::Volatile(($base as usize + $e) as *mut $t); 263 | )* 264 | ); 265 | } 266 | -------------------------------------------------------------------------------- /dev/htu21d.rs: -------------------------------------------------------------------------------- 1 | //! HTU21D temperature and humidity sensor. 2 | //! 3 | //! This module provides a driver for 4 | //! [HTU21D](https://cdn-shop.adafruit.com/datasheets/1899_HTU21D.pdf) 5 | //! sensor. 6 | use super::i2c; 7 | 8 | use core::marker::PhantomData; 9 | use core::pin::Pin; 10 | use core::task::Context; 11 | 12 | use futures::{Future, Poll}; 13 | 14 | #[allow(missing_debug_implementations)] 15 | pub struct Htu21d { 16 | i2c: &'static i2c::I2cBus, 17 | } 18 | 19 | impl Htu21d { 20 | pub const fn new(i2c: &'static i2c::I2cBus) -> Htu21d { 21 | Htu21d { i2c } 22 | } 23 | 24 | pub fn soft_reset(&'static self) -> Htu21dCommand { 25 | Htu21dCommand::StartTransfer(self.i2c.start_transfer(), SOFT_RESET_CMD.as_ptr()) 26 | } 27 | 28 | pub fn read_temperature_hold_master(&'static self) -> Htu21dCommand { 29 | Htu21dCommand::StartTransfer( 30 | self.i2c.start_transfer(), 31 | READ_TEMP_HOLD_MASTER_CMD.as_ptr(), 32 | ) 33 | } 34 | 35 | pub fn read_humidity_hold_master(&'static self) -> Htu21dCommand { 36 | Htu21dCommand::StartTransfer(self.i2c.start_transfer(), READ_HUM_HOLD_MASTER_CMD.as_ptr()) 37 | } 38 | } 39 | 40 | /// A marker for a measurement that holds master. 41 | #[derive(Debug)] 42 | pub struct HoldMaster; 43 | 44 | /// A marker for a measurement that does not holds master. 45 | #[derive(Debug)] 46 | pub struct NoHoldMaster; 47 | 48 | #[derive(Debug, Copy, Clone)] 49 | pub struct Reset; 50 | 51 | #[derive(Debug, Copy, Clone)] 52 | pub struct Temperature(u16); 53 | 54 | impl Temperature { 55 | /// Return raw sample from the sensor. 56 | /// 57 | /// The conversion formula must be applied to receive degrees 58 | /// celsius. 59 | pub const fn raw(self) -> u16 { 60 | self.0 61 | } 62 | 63 | /// Return temperature in degrees celsius. 64 | #[allow(clippy::float_arithmetic)] 65 | // f32::from is not constant 66 | #[allow(clippy::cast_lossless)] 67 | pub const fn celsius(self) -> f32 { 68 | -46.85 + 175.72 * ((self.0 & !0x3) as f32) / ((1 << 16) as f32) 69 | } 70 | 71 | /// Temperature in milliseconds. 72 | // i64::from is not constant 73 | #[allow(clippy::cast_lossless)] 74 | pub const fn millicelsius(self) -> i64 { 75 | -46_850 + ((175_720 * ((self.0 & !0x3) as i64)) >> 16) 76 | } 77 | } 78 | 79 | impl From for Temperature { 80 | fn from(sample: u16) -> Temperature { 81 | Temperature(sample) 82 | } 83 | } 84 | 85 | impl ::core::fmt::Display for Temperature { 86 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { 87 | let mc = self.millicelsius(); 88 | write!(f, "{}.{:03}", mc / 1000, mc % 1000) 89 | } 90 | } 91 | 92 | #[derive(Debug, Copy, Clone)] 93 | pub struct Humidity(u16); 94 | 95 | impl Humidity { 96 | pub const fn raw(self) -> u16 { 97 | self.0 98 | } 99 | 100 | #[allow(clippy::float_arithmetic)] 101 | // f32::from is not constant 102 | #[allow(clippy::cast_lossless)] 103 | pub const fn percents(self) -> f32 { 104 | -6.0 + 125.0 * ((self.0 & !0x3) as f32) / ((1 << 16) as f32) 105 | } 106 | 107 | // i64::from is not constant 108 | #[allow(clippy::cast_lossless)] 109 | pub const fn millipercents(self) -> i64 { 110 | -6_000 + ((125_000 * ((self.0 & !0x3) as i64)) >> 16) 111 | } 112 | } 113 | 114 | impl From for Humidity { 115 | fn from(sample: u16) -> Humidity { 116 | Humidity(sample) 117 | } 118 | } 119 | 120 | impl ::core::fmt::Display for Humidity { 121 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { 122 | let mp = self.millipercents(); 123 | write!(f, "{}.{:03}", mp / 1000, mp % 1000) 124 | } 125 | } 126 | 127 | #[derive(Debug)] 128 | pub enum Htu21dError { 129 | LockError, 130 | I2cError(i2c::Error), 131 | } 132 | 133 | impl From<()> for Htu21dError { 134 | fn from(_: ()) -> Htu21dError { 135 | Htu21dError::LockError 136 | } 137 | } 138 | 139 | impl From for Htu21dError { 140 | fn from(err: i2c::Error) -> Htu21dError { 141 | Htu21dError::I2cError(err) 142 | } 143 | } 144 | 145 | const HTU21D_ADDRESS: u16 = 0x80; 146 | 147 | const READ_TEMP_HOLD_MASTER_CMD: [u8; 1] = [0xE3]; 148 | const READ_HUM_HOLD_MASTER_CMD: [u8; 1] = [0xE5]; 149 | #[allow(dead_code)] 150 | const READ_TEMP_NO_HOLD_MASTER_CMD: [u8; 1] = [0xF3]; 151 | #[allow(dead_code)] 152 | const READ_HUM_NO_HOLD_MASTER_CMD: [u8; 1] = [0xF5]; 153 | #[allow(dead_code)] 154 | const WRITE_USER_CMD: [u8; 1] = [0xE6]; 155 | #[allow(dead_code)] 156 | const READ_USER_CMD: [u8; 1] = [0xE7]; 157 | const SOFT_RESET_CMD: [u8; 1] = [0xFE]; 158 | 159 | static mut __READ_BUFFER: [u8; 3] = [0; 3]; 160 | 161 | #[allow(missing_debug_implementations)] 162 | pub enum Htu21dCommand { 163 | StartTransfer(i2c::StartTransferFuture, *const u8), 164 | CmdTransmission(i2c::Transmission<'static>), 165 | ResultTransmission(i2c::Transmission<'static>), 166 | Done(u16, PhantomData<(H, R)>), 167 | } 168 | 169 | impl Unpin for Htu21dCommand {} 170 | 171 | impl Future for Htu21dCommand 172 | where 173 | T: From + Copy, 174 | { 175 | type Output = Result; 176 | 177 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 178 | use self::Htu21dCommand::*; 179 | 180 | let this = &mut *self; 181 | 182 | loop { 183 | *this = match this { 184 | StartTransfer(ref mut start_transfer, ref cmd) => { 185 | let i2c = ready!(Pin::new(start_transfer).poll(cx)); 186 | CmdTransmission(i2c.master_transmitter_raw(HTU21D_ADDRESS, *cmd, 1)) 187 | } 188 | CmdTransmission(ref mut transmission) => { 189 | let (i2c, _buf) = try_ready!(Pin::new(transmission).poll(cx)); 190 | ResultTransmission(i2c.master_receiver_raw( 191 | HTU21D_ADDRESS, 192 | unsafe { &mut __READ_BUFFER }.as_mut_ptr(), 193 | unsafe { &__READ_BUFFER }.len(), 194 | )) 195 | } 196 | ResultTransmission(ref mut transmission) => { 197 | let (mut i2c, buf) = try_ready!(Pin::new(transmission).poll(cx)); 198 | i2c.stop(); 199 | Done((u16::from(buf[0]) << 8) | u16::from(buf[1]), PhantomData) 200 | } 201 | Done(sample, _) => { 202 | return Poll::Ready(Ok(::from(*sample))); 203 | } 204 | }; 205 | } 206 | } 207 | } 208 | 209 | impl Future for Htu21dCommand { 210 | type Output = Result; 211 | 212 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 213 | use self::Htu21dCommand::*; 214 | 215 | let this = &mut *self; 216 | 217 | loop { 218 | *this = match this { 219 | StartTransfer(ref mut start_transfer, ref cmd) => { 220 | let transfer = ready!(Pin::new(start_transfer).poll(cx)); 221 | CmdTransmission(transfer.master_transmitter_raw(HTU21D_ADDRESS, *cmd, 1)) 222 | } 223 | CmdTransmission(ref mut transmission) => { 224 | let (mut i2c, _buf) = try_ready!(Pin::new(transmission).poll(cx)); 225 | i2c.stop(); 226 | Done(0, PhantomData) 227 | } 228 | Done(_, _) => { 229 | return Poll::Ready(Ok(Reset)); 230 | } 231 | _ => unsafe { 232 | ::core::intrinsics::unreachable(); 233 | }, 234 | }; 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /stm32f4/isr_vector.rs: -------------------------------------------------------------------------------- 1 | //! Interrupt Service Routines vector. 2 | //! 3 | //! This module defines order of ISRs in the vector. The vector is 4 | //! installed in appropriate place in the linker script. 5 | 6 | // The Void type has 0 size. It's used here to allow aliasing of 7 | // __data/bss_start/end. Otherwise, compiler may generate incorrect 8 | // code and the board will hang. 9 | enum Void {} 10 | 11 | extern "C" { 12 | static mut __init_data_start: u32; 13 | static mut __data_start: Void; 14 | static mut __data_end: Void; 15 | static mut __bss_start: Void; 16 | static mut __bss_end: Void; 17 | 18 | fn kmain(); 19 | } 20 | 21 | /// Reset handler. It copies `.data` segment, initializes `.bss` to 22 | /// zeros, and calls `kmain()`. 23 | #[no_mangle] 24 | // allow casting `*mut Void` to `*mut u32` 25 | #[allow(clippy::cast_ptr_alignment)] 26 | pub unsafe extern "C" fn __isr_reset() { 27 | let mut to = &mut __data_start as *mut Void as *mut u32; 28 | let data_end = &mut __data_end as *mut Void as *mut u32; 29 | 30 | let mut from = &mut __init_data_start as *mut u32; 31 | 32 | while to != data_end { 33 | *to = *from; 34 | 35 | to = to.offset(1); 36 | from = from.offset(1); 37 | } 38 | 39 | to = &mut __bss_start as *mut Void as *mut u32; 40 | let bss_end = &mut __bss_end as *mut Void as *mut u32; 41 | 42 | while to < bss_end { 43 | *to = 0; 44 | 45 | to = to.offset(1); 46 | } 47 | 48 | kmain(); 49 | 50 | panic!("kmain returned!"); 51 | } 52 | 53 | /// Default ISR. It just panics. 54 | #[cfg(target_arch = "arm")] 55 | #[no_mangle] 56 | pub unsafe extern "C" fn __isr_default() { 57 | let ipsr: u32; 58 | asm!("mrs $0, IPSR" : "=r" (ipsr)); 59 | if ipsr >= 16 { 60 | panic!("Unknown ISR handler: {} (IRQ {})!", ipsr, ipsr - 16); 61 | } else { 62 | panic!("Unknown ISR handler: {}!", ipsr); 63 | } 64 | } 65 | 66 | extern "C" { 67 | static __stack_end: u32; 68 | pub fn __isr_nmi(); 69 | pub fn __isr_hardfault(); 70 | pub fn __isr_memmanage(); 71 | pub fn __isr_busfault(); 72 | pub fn __isr_usagefault(); 73 | pub fn __isr_svc(); 74 | pub fn __isr_debugmon(); 75 | pub fn __isr_pendsv(); 76 | pub fn __isr_systick(); 77 | pub fn __isr_wwdg(); 78 | pub fn __isr_pvd(); 79 | pub fn __isr_tamp_stamp(); 80 | pub fn __isr_rtc_wkup(); 81 | pub fn __isr_flash(); 82 | pub fn __isr_rcc(); 83 | pub fn __isr_exti0(); 84 | pub fn __isr_exti1(); 85 | pub fn __isr_exti2(); 86 | pub fn __isr_exti3(); 87 | pub fn __isr_exti4(); 88 | pub fn __isr_dma1_stream0(); 89 | pub fn __isr_dma1_stream1(); 90 | pub fn __isr_dma1_stream2(); 91 | pub fn __isr_dma1_stream3(); 92 | pub fn __isr_dma1_stream4(); 93 | pub fn __isr_dma1_stream5(); 94 | pub fn __isr_dma1_stream6(); 95 | pub fn __isr_adc(); 96 | pub fn __isr_can1_tx(); 97 | pub fn __isr_can1_rx0(); 98 | pub fn __isr_can1_rx1(); 99 | pub fn __isr_can1_sce(); 100 | pub fn __isr_exti9_5(); 101 | pub fn __isr_tim1_brk_tim9(); 102 | pub fn __isr_tim1_up_tim10(); 103 | pub fn __isr_tim1_trg_com_tim11(); 104 | pub fn __isr_tim1_cc(); 105 | pub fn __isr_tim2(); 106 | pub fn __isr_tim3(); 107 | pub fn __isr_tim4(); 108 | pub fn __isr_i2c1_ev(); 109 | pub fn __isr_i2c1_er(); 110 | pub fn __isr_i2c2_ev(); 111 | pub fn __isr_i2c2_er(); 112 | pub fn __isr_spi1(); 113 | pub fn __isr_spi2(); 114 | pub fn __isr_usart1(); 115 | pub fn __isr_usart2(); 116 | pub fn __isr_usart3(); 117 | pub fn __isr_exti15_10(); 118 | pub fn __isr_rtc_alarm(); 119 | pub fn __isr_otg_fs_wkup(); 120 | pub fn __isr_tim8_brk_tim12(); 121 | pub fn __isr_tim8_up_tim13(); 122 | pub fn __isr_tim8_trg_com_tim14(); 123 | pub fn __isr_tim8_cc(); 124 | pub fn __isr_dma1_stream7(); 125 | pub fn __isr_fsmc(); 126 | pub fn __isr_sdio(); 127 | pub fn __isr_tim5(); 128 | pub fn __isr_spi3(); 129 | pub fn __isr_uart4(); 130 | pub fn __isr_uart5(); 131 | pub fn __isr_tim6_dac(); 132 | pub fn __isr_tim7(); 133 | pub fn __isr_dma2_stream0(); 134 | pub fn __isr_dma2_stream1(); 135 | pub fn __isr_dma2_stream2(); 136 | pub fn __isr_dma2_stream3(); 137 | pub fn __isr_dma2_stream4(); 138 | pub fn __isr_eth(); 139 | pub fn __isr_eth_wkup(); 140 | pub fn __isr_can2_tx(); 141 | pub fn __isr_can2_rx0(); 142 | pub fn __isr_can2_rx1(); 143 | pub fn __isr_can2_sce(); 144 | pub fn __isr_otg_fs(); 145 | pub fn __isr_dma2_stream5(); 146 | pub fn __isr_dma2_stream6(); 147 | pub fn __isr_dma2_stream7(); 148 | pub fn __isr_usart6(); 149 | pub fn __isr_i2c3_ev(); 150 | pub fn __isr_i2c3_er(); 151 | pub fn __isr_otg_hs_ep1_out(); 152 | pub fn __isr_otg_hs_ep1_in(); 153 | pub fn __isr_otg_hs_wkup(); 154 | pub fn __isr_otg_hs(); 155 | pub fn __isr_dcmi(); 156 | pub fn __isr_cryp(); 157 | pub fn __isr_hash_rng(); 158 | pub fn __isr_fpu(); 159 | } 160 | 161 | #[no_mangle] 162 | #[link_section = ".stack_end"] 163 | pub static STACK_END: &'static u32 = unsafe { &__stack_end }; 164 | 165 | #[no_mangle] 166 | #[link_section = ".isr_vector"] 167 | pub static ISR_VECTOR: [Option; 97] = [ 168 | Some(__isr_reset), 169 | Some(__isr_nmi), 170 | Some(__isr_hardfault), 171 | Some(__isr_memmanage), 172 | Some(__isr_busfault), 173 | Some(__isr_usagefault), 174 | None, 175 | None, 176 | None, 177 | None, 178 | Some(__isr_svc), 179 | Some(__isr_debugmon), 180 | None, 181 | Some(__isr_pendsv), 182 | Some(__isr_systick), 183 | Some(__isr_wwdg), 184 | Some(__isr_pvd), 185 | Some(__isr_tamp_stamp), 186 | Some(__isr_rtc_wkup), 187 | Some(__isr_flash), 188 | Some(__isr_rcc), 189 | Some(__isr_exti0), 190 | Some(__isr_exti1), 191 | Some(__isr_exti2), 192 | Some(__isr_exti3), 193 | Some(__isr_exti4), 194 | Some(__isr_dma1_stream0), 195 | Some(__isr_dma1_stream1), 196 | Some(__isr_dma1_stream2), 197 | Some(__isr_dma1_stream3), 198 | Some(__isr_dma1_stream4), 199 | Some(__isr_dma1_stream5), 200 | Some(__isr_dma1_stream6), 201 | Some(__isr_adc), 202 | Some(__isr_can1_tx), 203 | Some(__isr_can1_rx0), 204 | Some(__isr_can1_rx1), 205 | Some(__isr_can1_sce), 206 | Some(__isr_exti9_5), 207 | Some(__isr_tim1_brk_tim9), 208 | Some(__isr_tim1_up_tim10), 209 | Some(__isr_tim1_trg_com_tim11), 210 | Some(__isr_tim1_cc), 211 | Some(__isr_tim2), 212 | Some(__isr_tim3), 213 | Some(__isr_tim4), 214 | Some(__isr_i2c1_ev), 215 | Some(__isr_i2c1_er), 216 | Some(__isr_i2c2_ev), 217 | Some(__isr_i2c2_er), 218 | Some(__isr_spi1), 219 | Some(__isr_spi2), 220 | Some(__isr_usart1), 221 | Some(__isr_usart2), 222 | Some(__isr_usart3), 223 | Some(__isr_exti15_10), 224 | Some(__isr_rtc_alarm), 225 | Some(__isr_otg_fs_wkup), 226 | Some(__isr_tim8_brk_tim12), 227 | Some(__isr_tim8_up_tim13), 228 | Some(__isr_tim8_trg_com_tim14), 229 | Some(__isr_tim8_cc), 230 | Some(__isr_dma1_stream7), 231 | Some(__isr_fsmc), 232 | Some(__isr_sdio), 233 | Some(__isr_tim5), 234 | Some(__isr_spi3), 235 | Some(__isr_uart4), 236 | Some(__isr_uart5), 237 | Some(__isr_tim6_dac), 238 | Some(__isr_tim7), 239 | Some(__isr_dma2_stream0), 240 | Some(__isr_dma2_stream1), 241 | Some(__isr_dma2_stream2), 242 | Some(__isr_dma2_stream3), 243 | Some(__isr_dma2_stream4), 244 | Some(__isr_eth), 245 | Some(__isr_eth_wkup), 246 | Some(__isr_can2_tx), 247 | Some(__isr_can2_rx0), 248 | Some(__isr_can2_rx1), 249 | Some(__isr_can2_sce), 250 | Some(__isr_otg_fs), 251 | Some(__isr_dma2_stream5), 252 | Some(__isr_dma2_stream6), 253 | Some(__isr_dma2_stream7), 254 | Some(__isr_usart6), 255 | Some(__isr_i2c3_ev), 256 | Some(__isr_i2c3_er), 257 | Some(__isr_otg_hs_ep1_out), 258 | Some(__isr_otg_hs_ep1_in), 259 | Some(__isr_otg_hs_wkup), 260 | Some(__isr_otg_hs), 261 | Some(__isr_dcmi), 262 | Some(__isr_cryp), 263 | Some(__isr_hash_rng), 264 | Some(__isr_fpu), 265 | ]; 266 | -------------------------------------------------------------------------------- /breactor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(integer_atomics)] 3 | #![feature(const_fn)] 4 | 5 | #[macro_use] 6 | extern crate futures; 7 | 8 | extern crate stm32f4; 9 | 10 | pub mod mutex; 11 | pub mod promise; 12 | pub mod start_send_all; 13 | pub mod start_send_all_string; 14 | mod waker; 15 | 16 | use crate::waker::new_task_waker; 17 | use core::cell::UnsafeCell; 18 | use core::pin::Pin; 19 | use core::sync::atomic::{AtomicU32, Ordering}; 20 | use core::task::Context; 21 | use core::u32; 22 | 23 | use futures::{Future, Poll}; 24 | 25 | pub static REACTOR: Reactor = Reactor::new(); 26 | 27 | // Id is stored internally as a mask. 28 | #[derive(Debug, PartialEq, Clone, Copy)] 29 | pub struct TaskId(u32); 30 | 31 | impl TaskId { 32 | /// Creates new unchecked task id. 33 | /// 34 | /// The argument must be lower than 32. 35 | pub const unsafe fn unsafe_new(id: u32) -> TaskId { 36 | TaskId(1 << id) 37 | } 38 | 39 | /// Creates new checked TaskId from priority. 40 | /// 41 | /// # Return values 42 | /// Returns `None` if id is too high. 43 | /// ``` 44 | /// assert_eq!(None, breactor::TaskId::new(32)); 45 | /// ``` 46 | /// 47 | /// On success, returns some value. 48 | /// ``` 49 | /// assert!(breactor::TaskId::new(31).is_some()); 50 | /// ``` 51 | pub fn new(id: u32) -> Option { 52 | 1_u32.checked_shl(id).map(TaskId) 53 | } 54 | 55 | const fn get_mask(self) -> u32 { 56 | self.0 57 | } 58 | } 59 | 60 | /// The reactor is an entity that controls execution of multiple 61 | /// tasks. 62 | /// 63 | /// There could be only one reactor in the application, as it relies 64 | /// on global values. 65 | /// 66 | /// Each task has an ID assigned. The ID plays two roles. First, it 67 | /// distinguishes tasks, therefore it must be unique. Second, it 68 | /// determines the priority. Higher ids mean higher priority. 69 | #[allow(missing_debug_implementations)] 70 | pub struct Reactor<'a> { 71 | // TODO(rasen): should this be atomic? 72 | // 73 | // As far as I see, this must only be read from the system thread 74 | // and not interrupts, so there is no concurrent access. 75 | // 76 | // On the other hand, if we're going for task preemption, a switch 77 | // might occur right when the value is changed (or tasks reads its 78 | // id), leading to inconsistencies. 79 | current_task_mask: AtomicU32, 80 | tasks: [UnsafeCell>>>; 32], 81 | 82 | /// This is a bread and butter of the reactor. 83 | /// 84 | /// This variable holds 32 individual bits, each representing a 85 | /// readiness state of the task with id equal to the bit 86 | /// number. (e.g., 0x05, binary 101, means tasks with id 0 and 2 87 | /// are ready to run.) 88 | /// 89 | /// Such representation allows selecting the task with highest 90 | /// priority by counting leading zeros (which is extremely 91 | /// efficient operation), and setting/resetting task status 92 | /// atomically. This all makes this reactor lock-free. 93 | ready_mask: AtomicU32, 94 | } 95 | 96 | unsafe impl<'a> Sync for Reactor<'a> {} 97 | 98 | impl<'a> Reactor<'a> { 99 | pub const fn new() -> Reactor<'a> { 100 | Reactor { 101 | current_task_mask: AtomicU32::new(0), 102 | // Because the trait Copy is not implemented for &mut 103 | // Future 104 | tasks: [ 105 | UnsafeCell::new(None), 106 | UnsafeCell::new(None), 107 | UnsafeCell::new(None), 108 | UnsafeCell::new(None), 109 | UnsafeCell::new(None), 110 | UnsafeCell::new(None), 111 | UnsafeCell::new(None), 112 | UnsafeCell::new(None), 113 | UnsafeCell::new(None), 114 | UnsafeCell::new(None), 115 | UnsafeCell::new(None), 116 | UnsafeCell::new(None), 117 | UnsafeCell::new(None), 118 | UnsafeCell::new(None), 119 | UnsafeCell::new(None), 120 | UnsafeCell::new(None), 121 | UnsafeCell::new(None), 122 | UnsafeCell::new(None), 123 | UnsafeCell::new(None), 124 | UnsafeCell::new(None), 125 | UnsafeCell::new(None), 126 | UnsafeCell::new(None), 127 | UnsafeCell::new(None), 128 | UnsafeCell::new(None), 129 | UnsafeCell::new(None), 130 | UnsafeCell::new(None), 131 | UnsafeCell::new(None), 132 | UnsafeCell::new(None), 133 | UnsafeCell::new(None), 134 | UnsafeCell::new(None), 135 | UnsafeCell::new(None), 136 | UnsafeCell::new(None), 137 | ], 138 | ready_mask: AtomicU32::new(0), 139 | } 140 | } 141 | 142 | /// Creates a reactor with a predefined set of tasks. 143 | pub const fn from_array( 144 | tasks: [UnsafeCell>>>; 32], 145 | ) -> Reactor<'a> { 146 | Reactor { 147 | current_task_mask: AtomicU32::new(0), 148 | tasks, 149 | 150 | // All tasks are ready. 151 | // 152 | // TODO(rasen): maybe allow user to specify the mask? 153 | ready_mask: AtomicU32::new(u32::MAX), 154 | } 155 | } 156 | 157 | /// Marks the given task as ready. 158 | pub fn set_task_ready(&self, id: TaskId) { 159 | self.ready_mask.fetch_or(id.get_mask(), Ordering::SeqCst); 160 | unsafe { stm32f4::__set_event() }; 161 | } 162 | 163 | pub fn get_current_task_mask(&self) -> u32 { 164 | self.current_task_mask.load(Ordering::SeqCst) 165 | } 166 | 167 | pub fn set_ready_task_mask(&self, mask: u32) { 168 | if mask != 0 { 169 | self.ready_mask.fetch_or(mask, Ordering::SeqCst); 170 | unsafe { stm32f4::__set_event() }; 171 | } 172 | } 173 | 174 | /// Returns true if any task is ready to be polled. 175 | pub fn is_ready(&self) -> bool { 176 | self.ready_mask.load(Ordering::SeqCst) != 0 177 | } 178 | 179 | /// Returns next task to run. 180 | fn select_next_task(&self) -> Option { 181 | let mask = self.ready_mask.load(Ordering::SeqCst); 182 | let zeros = mask.leading_zeros(); 183 | if zeros == 32 { 184 | None 185 | } else { 186 | Some(31 - zeros) 187 | } 188 | } 189 | 190 | /// Runs until all tasks get blocked. 191 | /// 192 | /// This allows putting processor into sleep when there is no job 193 | /// to do. 194 | /// 195 | /// This function is unsafe because the caller must ensure that 196 | /// only a single thread calls run at the same time. 197 | pub unsafe fn run(&self) { 198 | while let Some(task_id) = self.select_next_task() { 199 | let task_mask = 1_u32 << task_id; 200 | self.ready_mask.fetch_and(!task_mask, Ordering::SeqCst); 201 | self.current_task_mask.store(task_mask, Ordering::SeqCst); 202 | 203 | let mtask = &mut *self.tasks[task_id as usize].get(); 204 | *mtask = match *mtask { 205 | Some(ref mut task) => { 206 | let waker = new_task_waker(task_mask); 207 | let mut cx = Context::from_waker(&waker); 208 | let res = task.as_mut().poll(&mut cx); 209 | match res { 210 | Poll::Pending => continue, 211 | // Remove task if has finished 212 | Poll::Ready(()) => None, 213 | } 214 | } 215 | None => { 216 | // Nothing to do 217 | continue; 218 | } 219 | }; 220 | } 221 | } 222 | 223 | /// Returns true if task was successfully added. 224 | /// Returns false if task_id is too high or already occupied. 225 | /// 226 | /// The caller must ensure it has unique write access to the 227 | /// reactor. 228 | pub unsafe fn add_task(&self, task_id: u32, f: Pin<&'a mut dyn Future>) -> bool { 229 | if task_id >= 32 { 230 | false 231 | } else { 232 | let ptr = self.tasks[task_id as usize].get(); 233 | if (*ptr).is_none() { 234 | *self.tasks[task_id as usize].get() = Some(f); 235 | self.set_task_ready(TaskId::unsafe_new(task_id)); 236 | true 237 | } else { 238 | false 239 | } 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /stm32f4/usart.rs: -------------------------------------------------------------------------------- 1 | //! Universal Synchronous Asynchronous Receiver Transmitter 2 | 3 | // Compiler thinks Bits0_5 is not camel case, but Bits05 is. 4 | #![allow(non_camel_case_types)] 5 | // allow `<< 0` 6 | #![allow(clippy::identity_op)] 7 | 8 | use core::fmt; 9 | 10 | use crate::volatile::RW; 11 | 12 | extern "C" { 13 | pub static USART1: Usart; 14 | pub static USART2: Usart; 15 | pub static USART3: Usart; 16 | } 17 | 18 | #[repr(C)] 19 | #[allow(missing_debug_implementations)] 20 | pub struct Usart { 21 | sr: RW, // 0x00 22 | dr: RW, // 0x04 23 | brr: RW, // 0x08 24 | cr1: RW, // 0x0C 25 | cr2: RW, // 0x10 26 | cr3: RW, // 0x14 27 | gtpr: RW, // 0x18 28 | } 29 | 30 | #[test] 31 | fn test_register_size() { 32 | assert_eq!(0x1C, ::core::mem::size_of::()); 33 | } 34 | 35 | #[allow(dead_code)] 36 | #[repr(u32)] 37 | enum Sr { 38 | PE = 1 << 0, 39 | FE = 1 << 1, 40 | NF = 1 << 2, 41 | ORE = 1 << 3, 42 | IDLE = 1 << 4, 43 | RXNE = 1 << 5, 44 | TC = 1 << 6, 45 | TXE = 1 << 7, 46 | LBD = 1 << 8, 47 | CTS = 1 << 9, 48 | } 49 | 50 | #[allow(dead_code)] 51 | #[repr(u32)] 52 | enum Brr { 53 | DIV_Fraction = 0x000F, 54 | DIV_Mantissa = 0xFFF0, 55 | } 56 | 57 | #[allow(dead_code)] 58 | #[repr(u32)] 59 | enum Cr1 { 60 | SBK = 1 << 0, 61 | RWU = 1 << 1, 62 | RE = 1 << 2, 63 | TE = 1 << 3, 64 | IDLEIE = 1 << 4, 65 | RXNEIE = 1 << 5, 66 | TCIE = 1 << 6, 67 | TXEIE = 1 << 7, 68 | PEIE = 1 << 8, 69 | PS = 1 << 9, 70 | PCE = 1 << 10, 71 | WAKE = 1 << 11, 72 | M = 1 << 12, 73 | /// USART Enable 74 | UE = 1 << 13, 75 | OVER8 = 1 << 15, 76 | } 77 | 78 | #[allow(dead_code)] 79 | #[repr(u32)] 80 | enum Cr2 { 81 | ADD = 0xF << 0, 82 | LBDL = 1 << 5, 83 | LBDIE = 1 << 6, 84 | LBCL = 1 << 8, 85 | CPHA = 1 << 9, 86 | CPOL = 1 << 10, 87 | CLKEN = 1 << 11, 88 | STOP = 0x3 << 12, 89 | LINEN = 1 << 14, 90 | } 91 | 92 | #[derive(Copy, Clone, Debug)] 93 | #[repr(u32)] 94 | pub enum StopBits { 95 | Bits1 = 0x0, 96 | Bits0_5 = 0x1, 97 | Bits2 = 0x2, 98 | Bits1_5 = 0x3, 99 | } 100 | 101 | #[allow(dead_code)] 102 | #[repr(u32)] 103 | enum Cr3 { 104 | EIE = 1 << 0, 105 | IREN = 1 << 1, 106 | IRLP = 1 << 2, 107 | HDSEL = 1 << 3, 108 | NACK = 1 << 4, 109 | SCEN = 1 << 5, 110 | DMAR = 1 << 6, 111 | DMAT = 1 << 7, 112 | RTSE = 1 << 8, 113 | CTSE = 1 << 9, 114 | CTSIE = 1 << 10, 115 | ONEBIT = 1 << 11, 116 | } 117 | 118 | #[allow(dead_code)] 119 | #[repr(u32)] 120 | enum Gtpr { 121 | PSC = 0x00FF, 122 | GT = 0xFF00, 123 | } 124 | 125 | #[derive(Copy, Clone, Debug)] 126 | pub enum FlowControl { 127 | No, 128 | } 129 | 130 | #[derive(Copy, Clone, Debug)] 131 | #[repr(u32)] 132 | pub enum DataBits { 133 | Bits8 = 0, 134 | Bits9 = Cr1::M as u32, 135 | } 136 | 137 | #[derive(Copy, Clone, Debug)] 138 | pub struct UsartConfig { 139 | pub data_bits: DataBits, 140 | pub stop_bits: StopBits, 141 | pub flow_control: FlowControl, 142 | pub baud_rate: u32, 143 | } 144 | 145 | #[derive(Copy, Clone, Debug)] 146 | #[repr(u32)] 147 | pub enum Interrupt { 148 | PE = 0x0028, 149 | TXE = 0x0727, 150 | TC = 0x0626, 151 | RXNE = 0x0525, 152 | ORE_RX = 0x0325, 153 | IDLE = 0x0424, 154 | LBD = 0x0846, 155 | CTS = 0x096A, 156 | ERR = 0x0060, 157 | ORE_ER = 0x0360, 158 | NE = 0x0260, 159 | FE = 0x0160, 160 | } 161 | 162 | #[derive(Copy, Clone, Debug)] 163 | #[repr(u32)] 164 | pub enum InterruptFlag { 165 | CTS = 0x0200, 166 | LBD = 0x0100, 167 | TXE = 0x0080, 168 | TC = 0x0040, 169 | RXNE = 0x0020, 170 | IDLE = 0x0010, 171 | ORE = 0x0008, 172 | NE = 0x0004, 173 | FE = 0x0002, 174 | PE = 0x0001, 175 | } 176 | 177 | impl Usart { 178 | /// Enables USART with given config. 179 | /// # Known bugs 180 | /// - No hardware flow control is supported. 181 | /// - Only works with default sysclk. 182 | /// - Generally, this driver is a piece of crap. 183 | pub fn enable(&self, config: &UsartConfig) { 184 | unsafe { 185 | self.cr2 186 | .update_with_mask(Cr2::STOP as u32, config.stop_bits as u32); 187 | self.cr1.update_with_mask( 188 | Cr1::M as u32 | Cr1::PCE as u32 | Cr1::TE as u32 | Cr1::RE as u32, 189 | config.data_bits as u32 | Cr1::TE as u32 | Cr1::RE as u32, 190 | ); 191 | self.cr3.clear_flag(0x3FF); // No Hardware Flow-Control 192 | self.brr.set(0x00F4_2400 / config.baud_rate); // Default SysClk Rate / Baud Rate 193 | 194 | // finally this enables the complete USART peripheral 195 | self.cr1.set_flag(Cr1::UE as u32); 196 | } 197 | } 198 | 199 | pub fn puts_synchronous(&self, s: &str) { 200 | for c in s.bytes() { 201 | self.put_char(u32::from(c)); 202 | } 203 | } 204 | 205 | pub fn put_bytes(&self, bytes: &[u8]) { 206 | for b in bytes { 207 | self.put_char(u32::from(*b)); 208 | } 209 | } 210 | 211 | pub fn put_char(&self, c: u32) { 212 | while !self.transmitter_empty() {} 213 | unsafe { 214 | self.dr.set(c); 215 | } 216 | } 217 | 218 | pub fn transmitter_empty(&self) -> bool { 219 | unsafe { self.sr.get() & Sr::TXE as u32 != 0 } 220 | } 221 | 222 | pub fn receiver_not_empty(&self) -> bool { 223 | unsafe { self.sr.get() & Sr::RXNE as u32 != 0 } 224 | } 225 | 226 | pub fn get_char(&self) -> u32 { 227 | while !self.receiver_not_empty() {} 228 | unsafe { self.dr.get() & 0xff } 229 | } 230 | 231 | #[allow(clippy::cast_possible_truncation)] // DR is 8-bit register 232 | pub unsafe fn get_unsafe(&self) -> u8 { 233 | self.dr.get() as u8 234 | } 235 | 236 | pub unsafe fn put_unsafe(&self, c: u8) { 237 | self.dr.set(u32::from(c)); 238 | } 239 | 240 | pub fn it_enable(&self, it: Interrupt) { 241 | self.it_set(it, true); 242 | } 243 | 244 | pub fn it_disable(&self, it: Interrupt) { 245 | self.it_set(it, false); 246 | } 247 | 248 | fn it_set(&self, it: Interrupt, enable: bool) { 249 | let itpos = it as u32 & 0x001F; 250 | let itmask = 0x01 << itpos; 251 | 252 | let usartreg = (it as u32 & 0xFF) >> 5; 253 | let reg = match usartreg { 254 | 0x01 => &self.cr1, 255 | 0x02 => &self.cr2, 256 | _ => &self.cr3, 257 | }; 258 | 259 | unsafe { 260 | if enable { 261 | reg.set_flag(itmask); 262 | } else { 263 | reg.clear_flag(itmask); 264 | } 265 | } 266 | } 267 | 268 | pub fn it_flag_status(&self, it: InterruptFlag) -> bool { 269 | unsafe { self.sr.get() & it as u32 != 0 } 270 | } 271 | 272 | pub fn it_clear_flag(&self, it: InterruptFlag) { 273 | unsafe { 274 | self.sr.set(u32::from(!(it as u16))); 275 | } 276 | } 277 | 278 | pub fn it_enabled(&self, it: Interrupt) -> bool { 279 | unsafe { 280 | let itpos = it as u32 & 0x001F; 281 | let itmask = 0x01 << itpos; 282 | 283 | let usartreg = (it as u8) >> 5; 284 | let reg = match usartreg { 285 | 0x01 => &self.cr1, 286 | 0x02 => &self.cr2, 287 | _ => &self.cr3, 288 | }; 289 | 290 | itmask & reg.get() != 0 291 | } 292 | } 293 | 294 | pub fn it_status(&self, it: Interrupt) -> bool { 295 | unsafe { 296 | let itpos = it as u32 & 0x001F; 297 | let mut itmask = 0x01 << itpos; 298 | 299 | let usartreg = (it as u8) >> 5; 300 | let reg = match usartreg { 301 | 0x01 => &self.cr1, 302 | 0x02 => &self.cr2, 303 | _ => &self.cr3, 304 | }; 305 | 306 | itmask &= reg.get(); 307 | 308 | let mut bitpos = it as u32 >> 8; 309 | bitpos = 0x01 << bitpos; 310 | bitpos &= self.sr.get(); 311 | 312 | bitpos != 0 && itmask != 0 313 | } 314 | } 315 | 316 | pub fn it_clear_pending(&self, it: Interrupt) { 317 | unsafe { 318 | let bitpos = it as u32 >> 8; 319 | let itmask = 1_u16 << bitpos; 320 | self.sr.set(u32::from(!itmask)); 321 | } 322 | } 323 | } 324 | 325 | // TODO(rasen): remove this implementation. Nobody should write 326 | // directly to the USART (except debugging). 327 | impl<'a> fmt::Write for &'a Usart { 328 | fn write_str(&mut self, s: &str) -> fmt::Result { 329 | self.puts_synchronous(s); 330 | Ok(()) 331 | } 332 | 333 | fn write_char(&mut self, c: char) -> fmt::Result { 334 | self.put_char(c as u32); 335 | Ok(()) 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /dev/i2c.rs: -------------------------------------------------------------------------------- 1 | //! I2C module adapter for use with futures. 2 | 3 | use core::cell::UnsafeCell; 4 | use core::marker::PhantomData; 5 | use core::pin::Pin; 6 | use core::task::Context; 7 | 8 | use stm32f4::i2c::{self, I2c}; 9 | 10 | use futures::{Future, FutureExt, Poll}; 11 | 12 | use breactor::mutex::{Mutex, MutexLock}; 13 | use breactor::promise::Promise; 14 | 15 | pub static I2C1_BUS: I2cBus = I2cBus::new(unsafe { &i2c::I2C1 }); 16 | pub static I2C2_BUS: I2cBus = I2cBus::new(unsafe { &i2c::I2C2 }); 17 | pub static I2C3_BUS: I2cBus = I2cBus::new(unsafe { &i2c::I2C3 }); 18 | 19 | #[allow(missing_debug_implementations)] 20 | pub struct I2cBus { 21 | i2c: &'static I2c, 22 | mutex: Mutex, 23 | slave_address: UnsafeCell, 24 | buffer: UnsafeCell<*mut u8>, 25 | buf_left: UnsafeCell, 26 | 27 | result: UnsafeCell>>, 28 | } 29 | 30 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 31 | pub enum Error { 32 | /// Failed to lock I2C bus. 33 | /// 34 | /// This should practically never occur. 35 | LockError, 36 | 37 | /// Acknowledgement failure. 38 | /// 39 | /// The device has not acknowledged its address or data byte. 40 | AcknowledgementFailure, 41 | 42 | ArbitrationLost, 43 | 44 | BusError, 45 | 46 | /// Unknown I2C error. 47 | /// 48 | /// The internal value is I2C event. 49 | Unknown(u32), 50 | } 51 | 52 | #[allow(missing_debug_implementations)] 53 | pub struct I2cTransfer { 54 | #[allow(dead_code)] 55 | lock: MutexLock<'static>, 56 | 57 | bus: &'static I2cBus, 58 | } 59 | 60 | unsafe impl Sync for I2cBus {} 61 | 62 | pub existential type StartTransferFuture: Future; 63 | 64 | impl I2cBus { 65 | const fn new(i2c: &'static I2c) -> Self { 66 | I2cBus { 67 | i2c, 68 | mutex: Mutex::new(), 69 | slave_address: UnsafeCell::new(0), 70 | buffer: UnsafeCell::new(::core::ptr::null_mut()), 71 | buf_left: UnsafeCell::new(0), 72 | result: UnsafeCell::new(unsafe { Promise::empty() }), 73 | } 74 | } 75 | 76 | pub fn start_transfer(&'static self) -> StartTransferFuture { 77 | self.mutex 78 | .lock() 79 | .map(move |lock| I2cTransfer { lock, bus: self }) 80 | } 81 | } 82 | 83 | #[allow(missing_debug_implementations)] 84 | pub struct Transmission<'a> { 85 | transfer: Option, 86 | 87 | data: *mut u8, 88 | size: usize, 89 | 90 | __phantom: PhantomData<&'a u8>, 91 | } 92 | 93 | impl<'a> Future for Transmission<'a> { 94 | type Output = Result<(I2cTransfer, &'a [u8]), Error>; 95 | 96 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 97 | let result = self.transfer.as_ref().unwrap().bus.result.get(); 98 | unsafe { 99 | try_ready!(Pin::new(&mut *result).poll(cx)); 100 | Poll::Ready(Ok(( 101 | self.transfer.take().unwrap(), 102 | ::core::slice::from_raw_parts(self.data, self.size), 103 | ))) 104 | } 105 | } 106 | } 107 | 108 | impl I2cTransfer { 109 | pub fn master_transmitter(self, addr: u16, data: &[u8]) -> Transmission { 110 | self.master_transmitter_raw(addr, data.as_ptr(), data.len()) 111 | } 112 | 113 | pub fn master_transmitter_raw<'a>( 114 | self, 115 | addr: u16, 116 | data_ptr: *const u8, 117 | data_size: usize, 118 | ) -> Transmission<'a> { 119 | unsafe { 120 | *self.bus.slave_address.get() = addr; 121 | *self.bus.buffer.get() = data_ptr as *mut u8; 122 | *self.bus.buf_left.get() = data_size; 123 | *self.bus.result.get() = Promise::new(); 124 | 125 | self.bus.i2c.generate_start(); 126 | 127 | self.bus.i2c.it_enable(i2c::Interrupt::Evt); 128 | self.bus.i2c.it_enable(i2c::Interrupt::Buf); 129 | self.bus.i2c.it_enable(i2c::Interrupt::Err); 130 | } 131 | 132 | Transmission { 133 | transfer: Some(self), 134 | data: data_ptr as *mut _, 135 | size: data_size, 136 | __phantom: PhantomData, 137 | } 138 | } 139 | 140 | pub fn master_receiver(self, addr: u16, data: &mut [u8]) -> Transmission { 141 | self.master_receiver_raw(addr, data.as_mut_ptr(), data.len()) 142 | } 143 | 144 | pub fn master_receiver_raw<'a>( 145 | self, 146 | addr: u16, 147 | data_ptr: *mut u8, 148 | data_size: usize, 149 | ) -> Transmission<'a> { 150 | unsafe { 151 | *self.bus.slave_address.get() = addr | 0x01; 152 | *self.bus.buffer.get() = data_ptr; 153 | *self.bus.buf_left.get() = data_size; 154 | *self.bus.result.get() = Promise::new(); 155 | 156 | self.bus.i2c.generate_start(); 157 | self.bus.i2c.set_acknowledge(true); 158 | 159 | self.bus.i2c.it_enable(i2c::Interrupt::Evt); 160 | self.bus.i2c.it_enable(i2c::Interrupt::Buf); 161 | self.bus.i2c.it_enable(i2c::Interrupt::Err); 162 | } 163 | 164 | Transmission { 165 | transfer: Some(self), 166 | data: data_ptr, 167 | size: data_size, 168 | __phantom: PhantomData, 169 | } 170 | } 171 | 172 | pub fn stop(&mut self) { 173 | // TODO: check START has been generated before? 174 | unsafe { 175 | self.bus.i2c.generate_stop(); 176 | } 177 | } 178 | } 179 | 180 | #[no_mangle] 181 | pub unsafe extern "C" fn __isr_i2c1_ev() { 182 | let bus = &I2C1_BUS; 183 | 184 | let event = bus.i2c.get_last_event(); 185 | 186 | if event == 0x30000 { 187 | // MSL, BUSY 188 | return; 189 | } 190 | if event == 0x0 { 191 | return; 192 | } 193 | 194 | match ::core::mem::transmute(event) { 195 | i2c::Event::MasterModeSelect => { 196 | let slave_address = *bus.slave_address.get(); 197 | // not really data, but who cares 198 | // TODO(ashmalko): handle ADDR10 199 | bus.i2c.send_data(slave_address as u8); 200 | } 201 | i2c::Event::MasterTransmitterModeSelected | i2c::Event::MasterReceiverModeSelected => { 202 | let buf_left = bus.buf_left.get(); 203 | if (*buf_left) == 1 { 204 | bus.i2c.set_acknowledge(false); 205 | } 206 | } 207 | i2c::Event::MasterByteTransmitted => { 208 | let buf_left = bus.buf_left.get(); 209 | 210 | if *buf_left == 0 { 211 | bus.i2c.it_disable(i2c::Interrupt::Evt); 212 | bus.i2c.it_disable(i2c::Interrupt::Buf); 213 | bus.i2c.it_disable(i2c::Interrupt::Err); 214 | 215 | let result = bus.result.get(); 216 | (*result).resolve(Ok(())); 217 | } 218 | } 219 | i2c::Event::MasterByteTransmitting => { 220 | let buffer = bus.buffer.get(); 221 | let buf_left = bus.buf_left.get(); 222 | 223 | if *buf_left > 0 { 224 | bus.i2c.send_data(**buffer); 225 | 226 | *buf_left -= 1; 227 | (*buffer) = (*buffer).offset(1); 228 | } 229 | } 230 | i2c::Event::MasterByteReceived => { 231 | let buffer = bus.buffer.get(); 232 | let buf_left = bus.buf_left.get(); 233 | 234 | debug_assert!(*buf_left > 0); 235 | 236 | **buffer = bus.i2c.receive_data(); 237 | 238 | *buf_left -= 1; 239 | (*buffer) = (*buffer).offset(1); 240 | 241 | if *buf_left == 1 { 242 | bus.i2c.set_acknowledge(false); 243 | } else if *buf_left == 0 { 244 | let result = bus.result.get(); 245 | (*result).resolve(Ok(())); 246 | 247 | bus.i2c.it_disable(i2c::Interrupt::Evt); 248 | bus.i2c.it_disable(i2c::Interrupt::Buf); 249 | bus.i2c.it_disable(i2c::Interrupt::Err); 250 | } 251 | } 252 | _ => { 253 | // TODO(ashmalko): this function should be rewritten to 254 | // check particular status flags, and not matching events 255 | // as whole. 256 | // panic!("__isr_i2c1_ev(): unknown event 0x{:x}", event); 257 | } 258 | } 259 | } 260 | 261 | #[no_mangle] 262 | pub unsafe extern "C" fn __isr_i2c1_er() { 263 | let bus = &I2C1_BUS; 264 | 265 | let event = bus.i2c.get_last_event(); 266 | 267 | bus.i2c.it_disable(i2c::Interrupt::Evt); 268 | bus.i2c.it_disable(i2c::Interrupt::Buf); 269 | bus.i2c.it_disable(i2c::Interrupt::Err); 270 | 271 | let error = if event & (i2c::Sr1Masks::AF as u32) != 0 { 272 | bus.i2c.it_clear_pending(i2c::Sr1Masks::AF as u32); 273 | Error::AcknowledgementFailure 274 | } else if event & (i2c::Sr1Masks::ARLO as u32) != 0 { 275 | bus.i2c.it_clear_pending(i2c::Sr1Masks::ARLO as u32); 276 | Error::ArbitrationLost 277 | } else if event & (i2c::Sr1Masks::BERR as u32) != 0 { 278 | bus.i2c.it_clear_pending(i2c::Sr1Masks::BERR as u32); 279 | Error::BusError 280 | } else { 281 | Error::Unknown(event) 282 | }; 283 | 284 | let result = bus.result.get(); 285 | (*result).resolve(Err(error)); 286 | } 287 | 288 | #[no_mangle] 289 | pub extern "C" fn __isr_i2c2_ev() {} 290 | 291 | #[no_mangle] 292 | pub extern "C" fn __isr_i2c2_er() {} 293 | 294 | #[no_mangle] 295 | pub extern "C" fn __isr_i2c3_ev() {} 296 | 297 | #[no_mangle] 298 | pub extern "C" fn __isr_i2c3_er() {} 299 | -------------------------------------------------------------------------------- /stm32f4/rcc.rs: -------------------------------------------------------------------------------- 1 | //! Reset and clock control. 2 | #![allow(clippy::identity_op)] 3 | 4 | use crate::volatile::{RES, RW}; 5 | 6 | extern "C" { 7 | pub static RCC: Rcc; 8 | } 9 | 10 | // TODO(rasen): allow changing this? 11 | /// Value of the Internal oscillator in Hz. 12 | const HSI_VALUE: u32 = 16_000_000; 13 | 14 | // TODO(rasen): allow changing this? 15 | /// Value of the External oscillator in Hz. 16 | const HSE_VALUE: u32 = 25_000_000; 17 | 18 | #[repr(C)] 19 | #[allow(missing_debug_implementations)] 20 | pub struct Rcc { 21 | cr: RW, // 0x00 22 | 23 | /// This register is used to configure the PLL clock outputs 24 | /// according to the formulas: 25 | /// 26 | /// - F_VCO_clock = F_PLL_clock_input * (PLLN / PLLM) 27 | /// - F_PLL_general_clock_output = F_VCO_clock / PLLP 28 | /// - F_USB_OTG_FS__SDIO__RNG_clock_output = F_VCO_clock / PLLQ 29 | pllcfgr: RW, // 0x04 30 | cfgr: RW, // 0x08 31 | cir: RW, // 0x0C 32 | ahb1rstr: RW, // 0x10 33 | ahb2rstr: RW, // 0x14 34 | ahb3rstr: RW, // 0x18 35 | _0: RES, // 0x1C 36 | apb1rstr: RW, // 0x20 37 | apb2rstr: RW, // 0x24 38 | _1: RES, // 0x28 39 | _2: RES, // 0x2C 40 | ahb1enr: RW, // 0x30 41 | ahb2enr: RW, // 0x34 42 | ahb3enr: RW, // 0x38 43 | _3: RES, // 0x3C 44 | apb1enr: RW, // 0x40 45 | apb2enr: RW, // 0x44 46 | _4: RES, // 0x48 47 | _5: RES, // 0x4C 48 | ahb1lpenr: RW, // 0x50 49 | ahb2lpenr: RW, // 0x54 50 | ahb3lpenr: RW, // 0x58 51 | _6: RES, // 0x5C 52 | apb1lpenr: RW, // 0x60 53 | apb2lpenr: RW, // 0x64 54 | _7: RES, // 0x68 55 | _8: RES, // 0x6C 56 | bdcr: RW, // 0x70 57 | csr: RW, // 0x74 58 | _9: RES, // 0x78 59 | _10: RES, // 0x7C 60 | sscgr: RW, // 0x80 61 | plli2scfgr: RW, // 0x84 62 | pllsaicfgr: RW, // 0x88 63 | dckcfgr: RW, // 0x8C 64 | } 65 | 66 | #[test] 67 | fn test_register_size() { 68 | assert_eq!(0x90, ::core::mem::size_of::()); 69 | } 70 | 71 | #[allow(dead_code)] 72 | #[derive(Copy, Clone)] 73 | #[repr(u32)] 74 | enum PllCfgrMask { 75 | // 5:0 76 | /// Division factor for the main PLL (PLL) and audio PLL (PLLI2S) 77 | /// input clock. 78 | PLLM = 0x3F << 0, 79 | 80 | // 14:6 81 | /// Mail PLL (PLL) multiplication factor for VCO. 82 | PLLN = 0xFF << 6, 83 | 84 | // 17:16 85 | /// Main PLL (PLL) division factor for main system clock. 86 | PLLP = 0x3 << 16, 87 | 88 | // 21:18 Reserved, must be kept at reset value. 89 | 90 | // 22 91 | /// Main PLL (PLL) and audio PLL (PLLI2S) entry clock source. 92 | PLLSRC = 0x1 << 22, 93 | 94 | // 23 Reserver, must be kept at reset value. 95 | 96 | // 27:24 97 | /// Main PLL (PLL) division factor for USB OTG FS, SDIO and random 98 | /// number generator clocks. 99 | PLLQ = 0xF << 24, 100 | // 31:28 Reserver, must be kept at reset value. 101 | } 102 | 103 | #[allow(dead_code)] 104 | #[derive(Copy, Clone)] 105 | #[repr(u32)] 106 | enum CfgrMask { 107 | // 1:0 108 | /// System clock switch 109 | SW = 0x3 << 0, 110 | 111 | // 3:2 112 | /// System clock switch status 113 | SWS = 0x3 << 2, 114 | 115 | // 7:4 116 | /// AHB prescaler 117 | HPRE = 0x7 << 4, 118 | 119 | // 9:8 reserved 120 | // 12:10 121 | /// APB Low speed prescaler (APB1) 122 | PPRE1 = 0x7 << 10, 123 | 124 | // 15:13 125 | /// APB high-speed prescaler (APB2) 126 | PPRE2 = 0x7 << 13, 127 | 128 | // 20:16 129 | /// HSE division factor for RTC clock 130 | RTCPRE = 0x1F << 16, 131 | 132 | // 22:21 133 | /// Microcontroller clock output 1 134 | MCO1 = 0x3 << 21, 135 | 136 | // 23 137 | /// I2S clock selection 138 | I2SSRC = 0x1 << 23, 139 | 140 | // 24:26 141 | /// MCO1 prescaler 142 | MCO1PRE = 0x7 << 24, 143 | 144 | // 27:29 145 | /// MCO2 prescaler 146 | MCO2PRE = 0x7 << 27, 147 | 148 | // 31:30 149 | /// Microcontroller clock output 2 [1:0] 150 | MCO2 = 0x3 << 30, 151 | } 152 | 153 | #[derive(Copy, Clone, Debug)] 154 | #[repr(u32)] 155 | pub enum Ahb1Enable { 156 | GPIOA = 1 << 0, 157 | GPIOB = 1 << 1, 158 | GPIOC = 1 << 2, 159 | GPIOD = 1 << 3, 160 | GPIOE = 1 << 4, 161 | GPIOF = 1 << 5, 162 | GPIOG = 1 << 6, 163 | GPIOH = 1 << 7, 164 | GPIOI = 1 << 8, 165 | GPIOJ = 1 << 9, 166 | GPIOK = 1 << 10, 167 | CRC = 1 << 12, 168 | BKPSRAM = 1 << 18, 169 | CCMDATARAM = 1 << 20, 170 | DMA1 = 1 << 21, 171 | DMA2 = 1 << 22, 172 | DMA2D = 1 << 23, 173 | ETHMAC = 1 << 25, 174 | ETHMACTX = 1 << 26, 175 | ETHMACRX = 1 << 27, 176 | ETHMACPTP = 1 << 28, 177 | OTGHS = 1 << 29, 178 | OTGHSULPI = 1 << 30, 179 | } 180 | 181 | #[derive(Copy, Clone, Debug)] 182 | #[repr(u32)] 183 | pub enum Ahb2Enable { 184 | DCMI = 1 << 0, 185 | CRYP = 1 << 4, 186 | HASH = 1 << 5, 187 | RNG = 1 << 6, 188 | OTGFS = 1 << 7, 189 | } 190 | 191 | #[derive(Copy, Clone, Debug)] 192 | #[repr(u32)] 193 | pub enum Ahb3Enable { 194 | FMC = 1 << 0, 195 | 196 | // This is added to avoid E0083: unsupported representation for 197 | // univariant enum 198 | __Dummy, 199 | } 200 | 201 | #[derive(Copy, Clone, Debug)] 202 | #[repr(u32)] 203 | pub enum Apb1Enable { 204 | TIM2 = 1 << 0, 205 | TIM3 = 1 << 1, 206 | TIM4 = 1 << 2, 207 | TIM5 = 1 << 3, 208 | TIM6 = 1 << 4, 209 | TIM7 = 1 << 5, 210 | TIM12 = 1 << 6, 211 | TIM13 = 1 << 7, 212 | TIM14 = 1 << 8, 213 | WWDG = 1 << 11, 214 | SPI2 = 1 << 14, 215 | SPI3 = 1 << 15, 216 | USART2 = 1 << 17, 217 | USART3 = 1 << 18, 218 | USART4 = 1 << 19, 219 | USART5 = 1 << 20, 220 | I2C1 = 1 << 21, 221 | I2C2 = 1 << 22, 222 | I2C3 = 1 << 23, 223 | CAN1 = 1 << 25, 224 | CAN2 = 1 << 26, 225 | PWR = 1 << 28, 226 | DAC = 1 << 29, 227 | UART7 = 1 << 30, 228 | UART8 = 1 << 31, 229 | } 230 | 231 | #[derive(Copy, Clone, Debug)] 232 | #[repr(u32)] 233 | pub enum Apb2Enable { 234 | TIM1 = 1 << 0, 235 | TIM8 = 1 << 1, 236 | USART1 = 1 << 4, 237 | USART6 = 1 << 5, 238 | ADC1 = 1 << 8, 239 | ADC2 = 1 << 9, 240 | ADC3 = 1 << 10, 241 | SDIO = 1 << 11, 242 | SPI1 = 1 << 12, 243 | SPI4 = 1 << 13, 244 | SYSCFG = 1 << 14, 245 | TIM9 = 1 << 16, 246 | TIM10 = 1 << 17, 247 | TIM11 = 1 << 18, 248 | SPI5 = 1 << 20, 249 | SPI6 = 1 << 21, 250 | SAI1 = 1 << 22, 251 | LTDC = 1 << 26, 252 | } 253 | 254 | #[allow(missing_debug_implementations)] 255 | pub struct Clocks { 256 | /// SYSCLK clock frequency expressed in Hz 257 | pub sysclk: u32, 258 | 259 | /// HCLK clock frequency expressed in Hz 260 | pub hclk: u32, 261 | 262 | /// PCLK1 clock frequency expressed in Hz 263 | pub pclk1: u32, 264 | 265 | /// PCLK2 clock frequency expressed in Hz 266 | pub pclk2: u32, 267 | } 268 | 269 | impl Rcc { 270 | pub fn ahb1_clock_enable(&self, value: Ahb1Enable) { 271 | unsafe { 272 | self.ahb1enr.update(|x| x | value as u32); 273 | } 274 | } 275 | 276 | pub fn ahb2_clock_enable(&self, value: Ahb2Enable) { 277 | unsafe { 278 | self.ahb2enr.update(|x| x | value as u32); 279 | } 280 | } 281 | 282 | pub fn ahb3_clock_enable(&self, value: Ahb3Enable) { 283 | unsafe { 284 | self.ahb3enr.update(|x| x | value as u32); 285 | } 286 | } 287 | 288 | pub fn apb1_clock_enable(&self, value: Apb1Enable) { 289 | unsafe { 290 | self.apb1enr.update(|x| x | value as u32); 291 | } 292 | } 293 | 294 | pub fn apb2_clock_enable(&self, value: Apb2Enable) { 295 | unsafe { 296 | self.apb2enr.update(|x| x | value as u32); 297 | } 298 | } 299 | 300 | pub fn clock_freqs(&self) -> Clocks { 301 | let cfgr = unsafe { self.cfgr.get() }; 302 | 303 | let sysclk = match cfgr & (CfgrMask::SWS as u32) { 304 | 0x00 => HSI_VALUE, 305 | 0x04 => HSE_VALUE, 306 | 0x08 => { 307 | // PLL_VCO = (HSE_VALUE or HSI_VALUE / PLLM) * PLLN 308 | // SYSCLK = PLL_VCO / PLLP 309 | 310 | let pllcfgr = unsafe { self.pllcfgr.get() }; 311 | 312 | let pllsource = (pllcfgr & PllCfgrMask::PLLSRC as u32) >> 22; 313 | let pllm = pllcfgr & PllCfgrMask::PLLM as u32; 314 | let plln = (pllcfgr & PllCfgrMask::PLLN as u32) >> 6; 315 | let pllp = (((pllcfgr & PllCfgrMask::PLLP as u32) >> 16) + 1) * 2; 316 | 317 | let pllvco_base = if pllsource != 0 { HSE_VALUE } else { HSI_VALUE }; 318 | let pllvco = pllvco_base / pllm * plln; 319 | 320 | pllvco / pllp 321 | } 322 | _ => { 323 | debug_assert!(false); 324 | // TODO(rasen): not applicable (assert? unreachable?) 325 | HSI_VALUE 326 | } 327 | }; 328 | 329 | // Compute HCLK, PCLK1 and PCLK2 clocks frequencies 330 | const APBAHB_PRESC_TABLE: [u8; 16] = [0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9]; 331 | 332 | let hclk = { 333 | let presc = APBAHB_PRESC_TABLE[((cfgr & CfgrMask::HPRE as u32) >> 4) as usize]; 334 | sysclk >> presc 335 | }; 336 | 337 | let pclk1 = { 338 | let presc = APBAHB_PRESC_TABLE[((cfgr & CfgrMask::PPRE1 as u32) >> 10) as usize]; 339 | hclk >> presc 340 | }; 341 | 342 | let pclk2 = { 343 | let presc = APBAHB_PRESC_TABLE[((cfgr & CfgrMask::PPRE2 as u32) >> 13) as usize]; 344 | hclk >> presc 345 | }; 346 | 347 | Clocks { 348 | sysclk, 349 | hclk, 350 | pclk1, 351 | pclk2, 352 | } 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /src/terminal.rs: -------------------------------------------------------------------------------- 1 | use crate::led; 2 | use crate::led_music; 3 | use core::task::Context; 4 | 5 | use core::pin::Pin; 6 | use futures::future::try_join; 7 | use futures::{Future, Poll, Sink, Stream, StreamExt, TryFutureExt, TryStreamExt}; 8 | 9 | use breactor::start_send_all_string::StartSendAllString; 10 | 11 | const PROMPT: &str = "> "; 12 | 13 | const HELP_MESSAGE: &str = "Available commands:\r 14 | hi -- welcomes you\r 15 | pony -- surprise!\r 16 | -3/+3 -- turn off/on LED3\r 17 | -4/+4 -- turn off/on LED4\r 18 | -5/+5 -- turn off/on LED5\r 19 | -6/+6 -- turn off/on LED6\r 20 | led-fun -- some fun with LEDs\r 21 | temp -- read temperature from HTU21D sensor\r 22 | panic -- throw a panic\r 23 | help -- print this help\r 24 | "; 25 | 26 | macro_rules! log { 27 | ( $( $x:expr ),* ) => { 28 | { 29 | use ::core::fmt::Write; 30 | let _ = write!(super::log::Logger::new(&super::USART2), $($x),*); 31 | } 32 | }; 33 | } 34 | 35 | // https://raw.githubusercontent.com/mbasaglia/ASCII-Pony/master/Ponies/vinyl-scratch-noglasses.txt 36 | // https://github.com/mbasaglia/ASCII-Pony/ 37 | const PONY: &str = "\r 38 | __..___\r 39 | _.-'____<'``\r 40 | ___.-`.-'` ```_'-.\r 41 | / \\.'` __.----'','/.._\\\r 42 | ( / \\_/` ,---''.' / `-'\r 43 | | | `,._\\ ,' /``''-.,`.\r 44 | /( '. \\ _____ ' ) `. `-;\r 45 | ( /\\ __/ __\\ / `: \\\r 46 | || (\\_ ( /.- | |'.| :\r 47 | _..._)`-._ || : \\ ,'\\ ((WW | \\W)j \\\r 48 | .-`.--''---._'-. |( (, \\ \\_\\_ / ``-. \\. )\r 49 | /.-'` __---__ '-.'. ' . \\`.`. \\__/- )`. |\r 50 | / ,' __`-. '.\\ V( \\ `-\\-,______.-' `. | `'\r 51 | / / .'` ```:. \\)___________/\\ .`. /.^. /| /. \\|\r 52 | ( ( / .' '-':-' \\|`.: (/ V )/ | )'\r 53 | ( ( ( ( / |'-.. ` \\ /, | '\r 54 | ( , \\ \\ \\ | _|``-| | | /\r 55 | \\ |. \\ \\-. \\ | (_| _| | |'\r 56 | \\| `. '. '.`.\\ | (_| |\r 57 | ' '.(`-._\\ ` / \\ / \\__/\r 58 | ` ..--' | /-,_______\\ \\\r 59 | .` _/ / | |\\ \\\r 60 | \\ / / | | `--, \\\r 61 | \\ | | | | / )\r 62 | \\__/| | | | ( |\r 63 | | | | | \\ |\r 64 | | \\ | \\ `.___/\r 65 | \\_______) \\_______)\r 66 | "; 67 | 68 | pub enum CommandResult { 69 | Sink(Option), 70 | Temperature( 71 | Option, 72 | ::futures::future::TryJoin< 73 | ::dev::htu21d::Htu21dCommand<::dev::htu21d::HoldMaster, ::dev::htu21d::Temperature>, 74 | ::dev::htu21d::Htu21dCommand<::dev::htu21d::HoldMaster, ::dev::htu21d::Humidity>, 75 | >, 76 | ), 77 | EchoChar(Option, u8), 78 | EchoCharStr(u8, StartSendAllString<'static, S>), 79 | FlushString(StartSendAllString<'static, S>), 80 | FlushPrompt(StartSendAllString<'static, S>), 81 | } 82 | 83 | impl CommandResult 84 | where 85 | S: Sink + Unpin, 86 | { 87 | pub fn echo_char(sink: S, c: u8) -> CommandResult { 88 | match c as char { 89 | // backspace 90 | '\u{8}' => CommandResult::EchoCharStr(c, StartSendAllString::new(sink, "\u{8} \u{8}")), 91 | '\r' => CommandResult::EchoCharStr(c, StartSendAllString::new(sink, "\r\n")), 92 | _ => CommandResult::EchoChar(Some(sink), c), 93 | } 94 | } 95 | 96 | pub fn flush(sink: S, string: &'static str) -> CommandResult { 97 | CommandResult::FlushString(StartSendAllString::new(sink, string)) 98 | } 99 | 100 | pub fn flush_prompt(sink: S) -> CommandResult { 101 | CommandResult::FlushPrompt(StartSendAllString::new(sink, PROMPT)) 102 | } 103 | 104 | pub fn sink(sink: S) -> CommandResult { 105 | CommandResult::Sink(Some(sink)) 106 | } 107 | 108 | pub fn temperature(sink: S) -> CommandResult { 109 | CommandResult::Temperature( 110 | Some(sink), 111 | try_join( 112 | super::HTU21D.read_temperature_hold_master(), 113 | super::HTU21D.read_humidity_hold_master(), 114 | ), 115 | ) 116 | } 117 | } 118 | 119 | impl Future for CommandResult 120 | where 121 | S: Sink + Unpin + 'static, 122 | { 123 | type Output = Result; 124 | 125 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 126 | let this = &mut *self; 127 | loop { 128 | *this = match this { 129 | CommandResult::EchoChar(ref mut msink, c) => { 130 | let sink = msink.as_mut().take().unwrap(); 131 | try_ready!(Pin::new(sink).poll_ready(cx)); 132 | 133 | let sink = msink.as_mut().take().unwrap(); 134 | return match Pin::new(sink).start_send(*c) { 135 | Ok(()) => { 136 | let sink = msink.take().unwrap(); 137 | Poll::Ready(Ok(sink)) 138 | } 139 | Err(err) => Poll::Ready(Err(err)), 140 | }; 141 | } 142 | CommandResult::EchoCharStr(c, ref mut f) => { 143 | let sink = try_ready!(Pin::new(f).poll(cx)); 144 | if *c == b'\r' { 145 | process_enter(sink) 146 | } else { 147 | return Poll::Ready(Ok(sink)); 148 | } 149 | } 150 | CommandResult::Temperature(ref mut sink, ref mut f) => { 151 | let res = ready!(Pin::new(f).poll(cx)); 152 | match res { 153 | Ok((temperature, humidity)) => { 154 | // TODO: don't use log 155 | log!( 156 | "Temperature: {} C Humidity: {}%\r\n", 157 | temperature, 158 | humidity 159 | ); 160 | CommandResult::flush_prompt(sink.take().unwrap()) 161 | } 162 | Err(err) => { 163 | log!("{:?}\r\n", err); 164 | CommandResult::flush(sink.take().unwrap(), "Temperature read error\r\n") 165 | } 166 | } 167 | } 168 | CommandResult::Sink(ref mut sink) => return Poll::Ready(Ok(sink.take().unwrap())), 169 | CommandResult::FlushString(ref mut f) => { 170 | let sink = try_ready!(Pin::new(f).poll(cx)); 171 | CommandResult::flush_prompt(sink) 172 | } 173 | CommandResult::FlushPrompt(ref mut f) => { 174 | let sink = try_ready!(Pin::new(f).poll(cx)); 175 | return Poll::Ready(Ok(sink)); 176 | } 177 | }; 178 | } 179 | } 180 | } 181 | 182 | /// Starts a terminal. 183 | pub fn run_terminal(stream: St, sink: Si) -> impl Future> + 'static 184 | where 185 | St: Stream + 'static, 186 | Si: Sink + Unpin + 'static, 187 | { 188 | StartSendAllString::new(sink, PROMPT) 189 | .and_then(|sink| stream.map(Ok).try_fold(sink, process_char)) 190 | } 191 | 192 | static mut COMMAND: [u8; 32] = [0; 32]; 193 | static mut CUR: usize = 0; 194 | 195 | /// Processes one character at a time. Calls `process_command` when 196 | /// user presses Enter or command is too long. 197 | fn process_char(sink: Si, c: u8) -> impl Future> + 'static 198 | where 199 | Si: Sink + Unpin + 'static, 200 | { 201 | let command = unsafe { &mut COMMAND }; 202 | let cur = unsafe { &mut CUR }; 203 | 204 | if c == 0x8 { 205 | // backspace 206 | if *cur != 0 { 207 | *cur -= 1; 208 | } else { 209 | // If there is nothing to delete, do nothing 210 | return CommandResult::sink(sink); 211 | } 212 | } else { 213 | command[*cur] = c; 214 | *cur += 1; 215 | 216 | if *cur == command.len() { 217 | // If command length is too long, emulate Enter was pressed 218 | return CommandResult::echo_char(sink, b'\r'); 219 | } 220 | } 221 | 222 | CommandResult::echo_char(sink, c) 223 | } 224 | 225 | fn process_enter(sink: Si) -> CommandResult 226 | where 227 | Si: Sink + Unpin + 'static, 228 | { 229 | let command = unsafe { &mut COMMAND }; 230 | let cur = unsafe { &mut CUR }; 231 | 232 | let command = &command[0..*cur - 1]; 233 | *cur = 0; 234 | 235 | match command { 236 | b"help" => CommandResult::flush(sink, HELP_MESSAGE), 237 | b"hi" => CommandResult::flush(sink, "Hi, there!\r\n"), 238 | b"pony" | b"p" => CommandResult::flush(sink, PONY), 239 | b"-3" => { 240 | led::LD3.turn_off(); 241 | CommandResult::flush_prompt(sink) 242 | } 243 | b"+3" => { 244 | led::LD3.turn_on(); 245 | CommandResult::flush_prompt(sink) 246 | } 247 | b"-4" => { 248 | led::LD4.turn_off(); 249 | CommandResult::flush_prompt(sink) 250 | } 251 | b"+4" => { 252 | led::LD4.turn_on(); 253 | CommandResult::flush_prompt(sink) 254 | } 255 | b"-5" => { 256 | led::LD5.turn_off(); 257 | CommandResult::flush_prompt(sink) 258 | } 259 | b"+5" => { 260 | led::LD5.turn_on(); 261 | CommandResult::flush_prompt(sink) 262 | } 263 | b"-6" => { 264 | led::LD6.turn_off(); 265 | CommandResult::flush_prompt(sink) 266 | } 267 | b"+6" => { 268 | led::LD6.turn_on(); 269 | CommandResult::flush_prompt(sink) 270 | } 271 | b"led-fun" => { 272 | led_music::led_fun(71000); 273 | CommandResult::flush_prompt(sink) 274 | } 275 | b"temp" | b"temperature" => CommandResult::temperature(sink), 276 | b"panic" => { 277 | panic!(); 278 | } 279 | b"" => CommandResult::flush_prompt(sink), 280 | _ => CommandResult::flush(sink, "Unknown command\r\n"), 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "autocfg" 5 | version = "0.1.2" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "bitflags" 10 | version = "1.0.4" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "bkernel" 15 | version = "0.0.1" 16 | dependencies = [ 17 | "breactor 0.0.1", 18 | "dev 0.0.1", 19 | "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "linkmem 0.1.0", 21 | "smalloc 0.0.1", 22 | "stm32f4 0.0.1", 23 | ] 24 | 25 | [[package]] 26 | name = "breactor" 27 | version = "0.0.1" 28 | dependencies = [ 29 | "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 30 | "stm32f4 0.0.1", 31 | ] 32 | 33 | [[package]] 34 | name = "cloudabi" 35 | version = "0.0.3" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | dependencies = [ 38 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 39 | ] 40 | 41 | [[package]] 42 | name = "dev" 43 | version = "0.0.1" 44 | dependencies = [ 45 | "breactor 0.0.1", 46 | "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 47 | "stm32f4 0.0.1", 48 | ] 49 | 50 | [[package]] 51 | name = "fuchsia-cprng" 52 | version = "0.1.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | 55 | [[package]] 56 | name = "futures-channel-preview" 57 | version = "0.3.0-alpha.16" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | dependencies = [ 60 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 61 | ] 62 | 63 | [[package]] 64 | name = "futures-core-preview" 65 | version = "0.3.0-alpha.16" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | 68 | [[package]] 69 | name = "futures-executor-preview" 70 | version = "0.3.0-alpha.16" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 77 | ] 78 | 79 | [[package]] 80 | name = "futures-io-preview" 81 | version = "0.3.0-alpha.16" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | dependencies = [ 84 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "futures-preview" 89 | version = "0.3.0-alpha.16" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 97 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 98 | ] 99 | 100 | [[package]] 101 | name = "futures-sink-preview" 102 | version = "0.3.0-alpha.16" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | dependencies = [ 105 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 106 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 107 | ] 108 | 109 | [[package]] 110 | name = "futures-util-preview" 111 | version = "0.3.0-alpha.16" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | dependencies = [ 114 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 116 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 117 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 119 | ] 120 | 121 | [[package]] 122 | name = "libc" 123 | version = "0.2.54" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | 126 | [[package]] 127 | name = "linkmem" 128 | version = "0.1.0" 129 | dependencies = [ 130 | "smalloc 0.0.1", 131 | ] 132 | 133 | [[package]] 134 | name = "pin-utils" 135 | version = "0.1.0-alpha.4" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | 138 | [[package]] 139 | name = "rand" 140 | version = "0.6.5" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | dependencies = [ 143 | "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 147 | "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 149 | "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 150 | "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 153 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 154 | ] 155 | 156 | [[package]] 157 | name = "rand_chacha" 158 | version = "0.1.1" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 163 | ] 164 | 165 | [[package]] 166 | name = "rand_core" 167 | version = "0.3.1" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | dependencies = [ 170 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 171 | ] 172 | 173 | [[package]] 174 | name = "rand_core" 175 | version = "0.4.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | 178 | [[package]] 179 | name = "rand_hc" 180 | version = "0.1.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | dependencies = [ 183 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 184 | ] 185 | 186 | [[package]] 187 | name = "rand_isaac" 188 | version = "0.1.1" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | dependencies = [ 191 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 192 | ] 193 | 194 | [[package]] 195 | name = "rand_jitter" 196 | version = "0.1.4" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | dependencies = [ 199 | "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 202 | ] 203 | 204 | [[package]] 205 | name = "rand_os" 206 | version = "0.1.3" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | dependencies = [ 209 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", 212 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 213 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 214 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 215 | ] 216 | 217 | [[package]] 218 | name = "rand_pcg" 219 | version = "0.1.2" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | dependencies = [ 222 | "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 224 | ] 225 | 226 | [[package]] 227 | name = "rand_xorshift" 228 | version = "0.1.1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "rdrand" 236 | version = "0.4.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | dependencies = [ 239 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 240 | ] 241 | 242 | [[package]] 243 | name = "smalloc" 244 | version = "0.0.1" 245 | dependencies = [ 246 | "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 247 | "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 248 | ] 249 | 250 | [[package]] 251 | name = "stm32f4" 252 | version = "0.0.1" 253 | 254 | [[package]] 255 | name = "winapi" 256 | version = "0.3.7" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | dependencies = [ 259 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 261 | ] 262 | 263 | [[package]] 264 | name = "winapi-i686-pc-windows-gnu" 265 | version = "0.4.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | 268 | [[package]] 269 | name = "winapi-x86_64-pc-windows-gnu" 270 | version = "0.4.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | 273 | [metadata] 274 | "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" 275 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 276 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 277 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 278 | "checksum futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4cd523712fc272e9b714669165a2832debee5a5b7e409bfccdc7c0d5cd0cf07a" 279 | "checksum futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "719770f328642b657b849856bb5a607db9538dd5bb3000122e5ead55d0a58c36" 280 | "checksum futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "315dc58c908535d059576a329b86cd185933433382cfcd394fb2fa353330de03" 281 | "checksum futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cca0bf7a1f39c9d32b797b0def93d5932aa71796236aad6b549bac6f7df159a3" 282 | "checksum futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfeac5f016a4b5835bb93eb7961f50a64f0e001207562703d9ddf4109d7b263" 283 | "checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" 284 | "checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" 285 | "checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6" 286 | "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 287 | "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 288 | "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 289 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 290 | "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" 291 | "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 292 | "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 293 | "checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 294 | "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 295 | "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 296 | "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 297 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 298 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 299 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 300 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 301 | -------------------------------------------------------------------------------- /dev/esp8266.rs: -------------------------------------------------------------------------------- 1 | //! ESP8266 AT command based driver. 2 | use core::array::FixedSizeArray; 3 | use core::marker::PhantomData; 4 | use core::pin::Pin; 5 | use core::str::FromStr; 6 | use core::task::Context; 7 | 8 | use futures::{Future, Poll, Sink, Stream, TryFutureExt}; 9 | 10 | use breactor::start_send_all_string::StartSendAllString; 11 | 12 | #[allow(unused)] 13 | macro_rules! debug_log { 14 | ( $( $x:expr ),* ) => { 15 | { 16 | use ::core::fmt::Write; 17 | let _lock = unsafe { ::stm32f4::IrqLock::new() }; 18 | 19 | let _ = write!(unsafe{&::stm32f4::usart::USART2}, $($x),*); 20 | } 21 | }; 22 | } 23 | 24 | #[allow(missing_debug_implementations)] 25 | pub struct Esp8266 + Sink> { 26 | usart: Channel, 27 | } 28 | 29 | #[derive(PartialEq, Eq, Debug)] 30 | pub enum Error { 31 | /// Generic error. 32 | Generic, 33 | /// Usart stream has finished. 34 | /// 35 | /// Practically, should never happen. 36 | UsartFinished, 37 | /// Usart stream has errored. 38 | UsartError, 39 | /// Internal buffer is too small to contain all ESP8266 output. 40 | BufferOverflow, 41 | } 42 | 43 | impl From> for Error { 44 | fn from(err: TakeUntilError) -> Error { 45 | match err { 46 | TakeUntilError::Finished(_) => Error::UsartFinished, 47 | TakeUntilError::StreamError(_, _) => Error::UsartError, 48 | TakeUntilError::BufferOverflow(_) => Error::BufferOverflow, 49 | } 50 | } 51 | } 52 | 53 | impl + Sink + Unpin> Esp8266 { 54 | /// Creates new ESP instance from a USART. 55 | /// 56 | /// # Examples 57 | /// ```no_run 58 | /// # #![feature(const_fn)] 59 | /// # extern crate dev; 60 | /// # extern crate stm32f4; 61 | /// # fn main() { 62 | /// # use ::dev::esp8266::Esp8266; 63 | /// # use ::dev::usart::Usart; 64 | /// static USART3: Usart<[u8; 32], [u8; 32]> = 65 | /// Usart::new(unsafe{&::stm32f4::usart::USART3}, [0; 32], [0; 32]); 66 | /// 67 | /// let esp = Esp8266::new(&USART3); 68 | /// # } 69 | /// ``` 70 | pub const fn new(usart: Channel) -> Esp8266 { 71 | Esp8266 { usart } 72 | } 73 | 74 | /// Check if the USART is connected to ESP8266 (actually, anything 75 | /// that accepts AT commands). 76 | /// 77 | /// # Examples 78 | /// ```no_run 79 | /// # #![feature(const_fn)] 80 | /// # #![feature(async_await)] 81 | /// # #![feature(await_macro)] 82 | /// # extern crate futures; 83 | /// # extern crate dev; 84 | /// # extern crate stm32f4; 85 | /// # fn main() { 86 | /// # use dev::esp8266::Esp8266; 87 | /// # use dev::usart::Usart; 88 | /// # use futures::{Poll, Future}; 89 | /// # async { 90 | /// static USART3: Usart<[u8; 32], [u8; 32]> = 91 | /// Usart::new(unsafe{&::stm32f4::usart::USART3}, [0; 32], [0; 32]); 92 | /// 93 | /// let mut esp = Esp8266::new(&USART3); 94 | /// assert_eq!(Ok(true), await!(esp.check_at())); 95 | /// 96 | /// # }; 97 | /// # } 98 | /// ``` 99 | pub fn check_at<'a>(&'a mut self) -> impl Future> + 'a { 100 | StartSendAllString::new(&mut self.usart, "AT\r\n") 101 | .map_err(|_err| Error::Generic) 102 | .and_then(|usart| { 103 | TakeUntil::new([0; 32], usart, [b"OK\r\n" as &[u8], b"ERROR\r\n" as &[u8]]) 104 | .map_err(|_err| Error::Generic) 105 | }) 106 | .map_ok(|(_buffer, _size, _m, _usart)| { 107 | // If any pattern matched, the other side understands 108 | // AT commands. 109 | true 110 | }) 111 | .map_err(|_err| Error::Generic) 112 | } 113 | 114 | /// List available access points. 115 | /// 116 | /// The resulting future returns a fixed-size array along with the 117 | /// actual number of access points returned from ESP8266. Note 118 | /// that the number may be higher than array requested. 119 | /// 120 | /// # Examples 121 | /// List up to 32 access points. 122 | /// 123 | /// ```no_run 124 | /// # #![feature(const_fn)] 125 | /// # extern crate futures; 126 | /// # extern crate dev; 127 | /// # extern crate stm32f4; 128 | /// # fn main() { 129 | /// # use dev::esp8266::{Esp8266, AccessPoint}; 130 | /// # use dev::usart::Usart; 131 | /// # use futures::{Future, FutureExt, TryFutureExt}; 132 | /// static USART3: Usart<[u8; 32], [u8; 32]> = 133 | /// Usart::new(unsafe{&::stm32f4::usart::USART3}, [0; 32], [0; 32]); 134 | /// 135 | /// let mut esp = Esp8266::new(&USART3); 136 | /// let mut aps = esp.list_aps::<[AccessPoint; 32]>() 137 | /// .and_then(|(aps, size)| { 138 | /// println!("Access points (total {}):", size); 139 | /// for i in 0 .. std::cmp::min(size, aps.len()) { 140 | /// println!("{:?}", aps[i]); 141 | /// } 142 | /// futures::future::ready(Ok(())) 143 | /// }); 144 | /// # } 145 | /// ``` 146 | // TODO(rasen): return Stream to leverage 147 | // incremental processing. This way, we can decrease buffer size. 148 | pub fn list_aps<'a, R>(&'a mut self) -> impl Future> + 'a 149 | where 150 | R: FixedSizeArray + 'a, 151 | { 152 | StartSendAllString::new(&mut self.usart, "AT+CWLAP\r\n") 153 | .map_err(|_| Error::Generic) 154 | .and_then(|usart| { 155 | TakeUntil::new([0; 32], usart, [b"\r\r\n" as &[u8]]).map_err(From::from) 156 | }) 157 | .and_then(|(_buffer, _size, _m, usart)| { 158 | TakeUntil::new( 159 | [0; 2048], 160 | usart, 161 | [b"\r\n\r\nOK\r\n" as &[u8], b"\r\n\r\nERROR\r\n"], 162 | ) 163 | .map_err(From::from) 164 | }) 165 | .map_ok(move |(buffer, size, m, _usart)| parse_ap_list::(&buffer[..size - m.len()])) 166 | } 167 | 168 | pub fn join_ap<'a>( 169 | &'a mut self, 170 | ap: &'a str, 171 | pass: &'a str, 172 | ) -> impl Future> + 'a { 173 | futures::future::lazy(move |_| Ok(&mut self.usart)) 174 | .and_then(|usart| StartSendAllString::new(usart, "AT+CWJAP=\"")) 175 | .and_then(move |usart| StartSendAllString::new(usart, ap)) 176 | .and_then(|usart| StartSendAllString::new(usart, "\",\"")) 177 | .and_then(move |usart| StartSendAllString::new(usart, pass)) 178 | .and_then(|usart| StartSendAllString::new(usart, "\"\r\n")) 179 | .map_err(|_err| Error::Generic) 180 | .and_then(|usart| { 181 | TakeUntil::new([0; 128], usart, [b"OK\r\n" as &[u8], b"ERROR\r\n" as &[u8]]) 182 | .map_err(|_err| Error::Generic) 183 | }) 184 | .map_ok(|(_buffer, _size, m, _usart)| match m { 185 | b"OK\r\n" => true, 186 | b"ERROR\r\n" => false, 187 | _ => unreachable!(), 188 | }) 189 | .map_err(|_err| Error::Generic) 190 | } 191 | } 192 | 193 | fn parse_ap_list(b: &[u8]) -> (A, usize) 194 | where 195 | A: FixedSizeArray, 196 | { 197 | let mut result: A = unsafe { ::core::mem::uninitialized() }; 198 | let mut cur = 0; 199 | 200 | for line in unsafe { ::core::str::from_utf8_unchecked(b) }.lines() { 201 | if cur < result.as_slice().len() { 202 | result.as_mut_slice()[cur] = parse_ap(line); 203 | } 204 | 205 | cur += 1; 206 | } 207 | 208 | (result, cur) 209 | } 210 | 211 | // TODO(rasen): error handling 212 | fn parse_ap(s: &str) -> AccessPoint { 213 | let mut s = s; 214 | // drop "+CWLAP:(" and final ")" 215 | s = &s[8..s.len() - 1]; 216 | 217 | // TODO(rasen): comma in ESSID is not allowed 218 | let mut s = s.split(','); 219 | 220 | let ecn = i32::from_str(s.next().unwrap_or("")).unwrap_or(0); 221 | 222 | let ssid_s = s.next().unwrap_or("\"\""); 223 | let ssid_s = &ssid_s[1..ssid_s.len() - 1]; 224 | let ssid_len = ssid_s.len(); 225 | let mut ssid: [u8; 32] = unsafe { ::core::mem::zeroed() }; 226 | (&mut ssid[..ssid_len]).clone_from_slice(&ssid_s.as_bytes()); 227 | 228 | let rssi = i32::from_str(s.next().unwrap_or("")).unwrap_or(0); 229 | 230 | let mac_s = s.next().unwrap_or("\"\""); 231 | let mut mac_parts = mac_s[1..mac_s.len() - 1] 232 | .split(':') 233 | .map(|hex| i32::from_str_radix(hex, 16).unwrap_or(0x00) as u8); 234 | let mut mac: [u8; 6] = [0; 6]; 235 | mac[0] = mac_parts.next().unwrap_or(0); 236 | mac[1] = mac_parts.next().unwrap_or(0); 237 | mac[2] = mac_parts.next().unwrap_or(0); 238 | mac[3] = mac_parts.next().unwrap_or(0); 239 | mac[4] = mac_parts.next().unwrap_or(0); 240 | mac[5] = mac_parts.next().unwrap_or(0); 241 | 242 | let ch = i32::from_str(s.next().unwrap_or("")).unwrap_or(0); 243 | 244 | let freq_offset = i32::from_str(s.next().unwrap_or("")).unwrap_or(0); 245 | 246 | let freq_calibration = i32::from_str(s.next().unwrap_or("")).unwrap_or(0); 247 | 248 | AccessPoint { 249 | ecn: unsafe { ::core::mem::transmute(ecn as u8) }, 250 | ssid_len: ssid_len as u8, 251 | ssid, 252 | rssi, 253 | mac, 254 | ch: ch as u8, 255 | freq_offset, 256 | freq_calibration, 257 | } 258 | } 259 | 260 | /// Encryption method used by Access Point. 261 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 262 | #[repr(u8)] 263 | pub enum EncryptionMethod { 264 | Open = 0, 265 | Wep = 1, 266 | WpaPsk = 2, 267 | Wpa2Psk = 3, 268 | WpaWpa2Psk = 4, 269 | Wpa2Enterprise = 5, 270 | } 271 | 272 | /// Access Point detected by ESP8266. 273 | pub struct AccessPoint { 274 | /// Encryption method. 275 | pub ecn: EncryptionMethod, 276 | 277 | pub ssid_len: u8, 278 | /// String parameter, SSID of the AP. 279 | /// 280 | /// Only first `ssid_len` bytes are valid. 281 | pub ssid: [u8; 32], 282 | 283 | /// Signal strength. 284 | pub rssi: i32, 285 | 286 | /// MAC address of the AP. 287 | // TODO(rasen): Create MAC structure 288 | pub mac: [u8; 6], 289 | 290 | /// Channel. 291 | pub ch: u8, 292 | 293 | /// Frequency offset of AP; unit: KHz. 294 | pub freq_offset: i32, 295 | 296 | /// Calibration for frequency offset. 297 | pub freq_calibration: i32, 298 | } 299 | 300 | impl AccessPoint { 301 | /// Returns SSID as a string. 302 | pub fn ssid(&self) -> &str { 303 | unsafe { ::core::str::from_utf8_unchecked(&self.ssid[..self.ssid_len as usize]) } 304 | } 305 | } 306 | 307 | impl ::core::fmt::Debug for AccessPoint { 308 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 309 | write!( 310 | f, 311 | "AccessPoint({:?}, \"{}\", {}, {:?}, {}, {}, {})", 312 | self.ecn, 313 | self.ssid(), 314 | self.rssi, 315 | // TODO(rasen): better MAC formatting 316 | self.mac, 317 | self.ch, 318 | self.freq_offset, 319 | self.freq_calibration 320 | ) 321 | } 322 | } 323 | 324 | #[allow(missing_debug_implementations)] 325 | struct TakeUntil<'a, A, S, M> { 326 | buffer: A, 327 | stream: Option, 328 | matches: M, 329 | cur: usize, 330 | __phantom: PhantomData<&'a u8>, 331 | } 332 | 333 | impl<'a, A, S, M> TakeUntil<'a, A, S, M> 334 | where 335 | A: FixedSizeArray, 336 | S: Stream + Unpin, 337 | M: FixedSizeArray<&'static [u8]>, 338 | { 339 | pub fn new(buffer: A, stream: S, matches: M) -> TakeUntil<'a, A, S, M> { 340 | TakeUntil { 341 | buffer, 342 | stream: Some(stream), 343 | matches, 344 | cur: 0, 345 | __phantom: PhantomData, 346 | } 347 | } 348 | } 349 | 350 | #[derive(PartialEq, Eq, Debug)] 351 | enum TakeUntilError { 352 | /// The stream has finished. 353 | Finished(S), 354 | 355 | /// Stream has errored while polling. 356 | StreamError(S, E), 357 | 358 | /// Provided buffer is too small. 359 | BufferOverflow(S), 360 | } 361 | 362 | impl<'a, A, S, M> Unpin for TakeUntil<'a, A, S, M> 363 | where 364 | A: FixedSizeArray, 365 | S: Stream + Unpin, 366 | M: FixedSizeArray<&'static [u8]>, 367 | { 368 | } 369 | 370 | impl<'a, A, S, M> Future for TakeUntil<'a, A, S, M> 371 | where 372 | A: FixedSizeArray, 373 | S: Stream + Unpin, 374 | M: FixedSizeArray<&'static [u8]>, 375 | { 376 | type Output = Result<(A, usize, &'static [u8], S), TakeUntilError>; 377 | 378 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 379 | loop { 380 | if self.cur >= self.buffer.as_slice().len() { 381 | return Poll::Ready(Err(TakeUntilError::BufferOverflow( 382 | self.stream.take().unwrap(), 383 | ))); 384 | } 385 | 386 | match Pin::new(self.stream.as_mut().take().unwrap()).poll_next(cx) { 387 | Poll::Ready(Some(c)) => { 388 | let cur = self.cur; 389 | self.buffer.as_mut_slice()[cur] = c; 390 | self.cur += 1; 391 | 392 | for m in self.matches.as_slice() { 393 | if self.buffer.as_slice()[..self.cur].ends_with(m) { 394 | let mut b: A = unsafe { ::core::mem::uninitialized() }; 395 | b.as_mut_slice()[..self.cur] 396 | .clone_from_slice(&self.buffer.as_slice()[..self.cur]); 397 | 398 | return Poll::Ready(Ok((b, self.cur, m, self.stream.take().unwrap()))); 399 | } 400 | } 401 | } 402 | 403 | Poll::Ready(None) => { 404 | return Poll::Ready(Err(TakeUntilError::Finished(self.stream.take().unwrap()))); 405 | } 406 | 407 | Poll::Pending => { 408 | return Poll::Pending; 409 | } 410 | } 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items, core_intrinsics, const_fn)] 2 | #![feature(fixed_size_array)] 3 | #![feature(alloc_error_handler)] 4 | #![cfg_attr(not(test), no_main)] 5 | #![cfg_attr(target_os = "none", no_std)] 6 | 7 | #[cfg(not(target_os = "none"))] 8 | extern crate core; 9 | 10 | extern crate dev; 11 | extern crate stm32f4; 12 | 13 | extern crate alloc; 14 | #[cfg(target_os = "none")] 15 | extern crate linkmem; 16 | #[cfg(target_os = "none")] 17 | extern crate smalloc; 18 | 19 | #[macro_use] 20 | extern crate futures; 21 | 22 | extern crate breactor; 23 | 24 | mod led; 25 | mod led_music; 26 | mod log; 27 | mod terminal; 28 | 29 | use core::pin::Pin; 30 | 31 | use futures::future; 32 | use futures::FutureExt; 33 | use futures::Poll; 34 | use futures::TryFutureExt; 35 | 36 | use stm32f4::gpio::{GPIO_B, GPIO_D}; 37 | use stm32f4::rcc::RCC; 38 | use stm32f4::timer::TIM2; 39 | use stm32f4::{gpio, nvic, rcc, timer, usart}; 40 | 41 | use ::breactor::start_send_all_string::StartSendAllString; 42 | 43 | use ::breactor::REACTOR; 44 | 45 | use ::dev::usart::Usart; 46 | 47 | use ::dev::htu21d::{Htu21d, Htu21dError}; 48 | 49 | use ::dev::cs43l22::Cs43l22; 50 | 51 | use ::dev::esp8266::{AccessPoint, Esp8266}; 52 | 53 | pub static USART3: Usart<[u8; 32], [u8; 32]> = 54 | Usart::new(unsafe { &::stm32f4::usart::USART3 }, [0; 32], [0; 32]); 55 | 56 | pub static mut ESP8266: Esp8266<&'static Usart<[u8; 32], [u8; 32]>> = Esp8266::new(&USART3); 57 | 58 | pub static USART2: Usart<[u8; 128], [u8; 32]> = 59 | Usart::new(unsafe { &::stm32f4::usart::USART2 }, [0; 128], [0; 32]); 60 | 61 | macro_rules! debug_log { 62 | ( $( $x:expr ),* ) => { 63 | { 64 | use ::core::fmt::Write; 65 | let _lock = unsafe { ::stm32f4::IrqLock::new() }; 66 | 67 | let _ = write!(unsafe{&::stm32f4::usart::USART2}, $($x),*); 68 | } 69 | }; 70 | } 71 | 72 | macro_rules! log { 73 | ( $( $x:expr ),* ) => { 74 | { 75 | use ::core::fmt::Write; 76 | let _ = write!(log::Logger::new(&USART2), $($x),*); 77 | } 78 | }; 79 | } 80 | 81 | static HTU21D: Htu21d = Htu21d::new(&::dev::i2c::I2C1_BUS); 82 | 83 | static mut CS43L22: Cs43l22 = Cs43l22::new(&::dev::i2c::I2C1_BUS, false); 84 | 85 | #[cfg(target_os = "none")] 86 | fn init_memory() { 87 | const HEAP_SIZE: usize = 64 * 1024; 88 | static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; 89 | 90 | ::linkmem::init(smalloc::Smalloc { 91 | start: unsafe { &mut HEAP }.as_mut_ptr(), 92 | size: HEAP_SIZE, 93 | }); 94 | } 95 | 96 | #[cfg(not(target_os = "none"))] 97 | fn init_memory() {} 98 | 99 | /// The main entry of the kernel. 100 | #[no_mangle] 101 | pub extern "C" fn kmain() -> ! { 102 | init_memory(); 103 | unsafe { 104 | init_usart2(); 105 | init_esp8266(); 106 | init_leds(); 107 | init_timer(); 108 | init_i2c(); 109 | init_rng(); 110 | } 111 | 112 | // Test that allocator works 113 | let mut b = ::alloc::boxed::Box::new(5); 114 | unsafe { 115 | ::core::intrinsics::volatile_store(&mut *b as *mut _, 4); 116 | } 117 | 118 | // unsafe { &mut ::dev::rng::RNG }.enable(); 119 | // let mut print_rng = unsafe { &mut ::dev::rng::RNG } 120 | // .for_each(|r| { 121 | // use core::fmt::Write; 122 | // let _ = writeln!(unsafe { &::stm32f4::usart::USART2 }, "RNG: {:?}\r", r); 123 | 124 | // futures::future::ready(()) 125 | // }) 126 | // .map(|_| ()); 127 | 128 | let mut terminal = StartSendAllString::new( 129 | &USART2, 130 | "\r\nWelcome to bkernel!\r\nType 'help' to get a list of available commands.\r\n", 131 | ) 132 | .and_then(|stdout| terminal::run_terminal(&USART2, stdout)) 133 | .map(|_| ()); 134 | 135 | let mut htu21d = HTU21D 136 | .soft_reset() 137 | .and_then(|_| { 138 | // This is needed because device is not instantly up after 139 | // reset, so we poll it, untill it ready. 140 | future::poll_fn(|cx| { 141 | match ready!(HTU21D.read_temperature_hold_master().poll_unpin(cx)) { 142 | Ok(temp) => Poll::Ready(Ok(temp)), 143 | // Acknowledge failure -> device is not ready -> retry 144 | Err(Htu21dError::I2cError(dev::i2c::Error::AcknowledgementFailure)) => { 145 | Poll::Pending 146 | } 147 | Err(x) => Poll::Ready(Err(x)), 148 | } 149 | }) 150 | }) 151 | .and_then(|temp| { 152 | HTU21D 153 | .read_humidity_hold_master() 154 | .map_ok(move |hum| (temp, hum)) 155 | }) 156 | .then(|x| { 157 | match x { 158 | Ok((temp, hum)) => log!("Temperature: {} C Humidity: {}%\r\n", temp, hum), 159 | Err(err) => log!("HTU21D error: {:?}\r\n", err), 160 | } 161 | 162 | future::ready(()) 163 | }); 164 | 165 | let mut cs43l22 = unsafe { &mut CS43L22 }.get_chip_id().then(|res| { 166 | match res { 167 | Ok(id) => { 168 | log!("CS43L22 CHIP ID: 0b{:b}\r\n", id); 169 | } 170 | Err(err) => { 171 | log!("Error: {:?}\r\n", err); 172 | } 173 | } 174 | 175 | future::ready(()) 176 | }); 177 | 178 | let mut esp8266 = unsafe { &mut ESP8266 } 179 | .check_at() 180 | .then(|x| { 181 | log!("\r\nESP CHECK AT: {:?}\r\n", x); 182 | future::ready(Ok(()) as Result<(), ()>) 183 | }) 184 | .then(|_| unsafe { &mut ESP8266 }.list_aps::<[AccessPoint; 32]>()) 185 | .and_then(|(aps, size)| { 186 | debug_log!("\r\nAccess points:\r\n"); 187 | for ap in &aps[0..::core::cmp::min(size, aps.len())] { 188 | debug_log!("{:?}\r\n", ap); 189 | } 190 | 191 | future::ready(Ok(())) 192 | }) 193 | .then(|_| unsafe { &mut ESP8266 }.join_ap("Rotem Indiana_Guest", "snickershock")) 194 | .and_then(|res| { 195 | debug_log!("Join status: {:?}", res); 196 | future::ready(Ok(())) 197 | }) 198 | .map_err(|err| { 199 | log!("\r\nESP8266 error: {:?}\r\n", err); 200 | }) 201 | .map(|_| ()); 202 | 203 | unsafe { 204 | let reactor = &REACTOR; 205 | 206 | // Trust me, I know what I'm doing with lifetime loundary here. 207 | // 208 | // The infinite loop below makes all values above it 209 | // effectively 'static. 210 | reactor.add_task(5, Pin::new_unchecked(lifetime_loundary(&mut terminal))); 211 | // reactor.add_task(4, Pin::new_unchecked(lifetime_loundary(&mut print_rng))); 212 | reactor.add_task(6, Pin::new_unchecked(lifetime_loundary(&mut htu21d))); 213 | reactor.add_task(2, Pin::new_unchecked(lifetime_loundary(&mut cs43l22))); 214 | reactor.add_task(1, Pin::new_unchecked(lifetime_loundary(&mut esp8266))); 215 | 216 | loop { 217 | reactor.run(); 218 | 219 | // SEV is issued when any task sets readiness to true. 220 | stm32f4::__wait_for_event(); 221 | } 222 | } 223 | } 224 | 225 | /// Extremely unsafe (probably even UB) 226 | unsafe fn lifetime_loundary<'a, 'b, T: ?Sized>(val: &'a mut T) -> &'b mut T { 227 | &mut *(val as *mut _) 228 | } 229 | 230 | unsafe fn init_timer() { 231 | RCC.apb1_clock_enable(rcc::Apb1Enable::TIM2); 232 | 233 | TIM2.init(&timer::TimInit { 234 | prescaler: 40000, 235 | counter_mode: timer::CounterMode::Up, 236 | period: 128, 237 | clock_division: timer::ClockDivision::Div1, 238 | repetition_counter: 0, 239 | }); 240 | 241 | TIM2.it_enable(timer::Dier::UIE); 242 | 243 | TIM2.enable(); 244 | 245 | nvic::init(&nvic::NvicInit { 246 | irq_channel: nvic::IrqChannel::TIM2, 247 | priority: 0, 248 | subpriority: 1, 249 | enable: true, 250 | }); 251 | } 252 | 253 | unsafe fn init_leds() { 254 | RCC.ahb1_clock_enable(rcc::Ahb1Enable::GPIOD); 255 | led::LD3.init(); 256 | led::LD4.init(); 257 | led::LD5.init(); 258 | led::LD6.init(); 259 | 260 | led::LD3.turn_on(); 261 | led::LD4.turn_on(); 262 | led::LD5.turn_on(); 263 | led::LD6.turn_on(); 264 | } 265 | 266 | unsafe fn init_usart2() { 267 | use ::stm32f4::usart::USART2; 268 | 269 | RCC.apb1_clock_enable(rcc::Apb1Enable::USART2); 270 | 271 | // Enable the peripheral clock for the pins used by USART2, PD5 272 | // for TX and PD6 for RX 273 | RCC.ahb1_clock_enable(rcc::Ahb1Enable::GPIOD); 274 | 275 | // This sequence sets up the TX and RX pins so they work correctly 276 | // with the USART2 peripheral 277 | GPIO_D.enable( 278 | 5, 279 | gpio::GpioConfig { 280 | mode: gpio::GpioMode::AF, 281 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 282 | otype: gpio::GpioOType::PUSH_PULL, 283 | pupd: gpio::GpioPuPd::PULL_UP, 284 | af: gpio::GpioAF::AF7, 285 | }, 286 | ); 287 | GPIO_D.enable( 288 | 6, 289 | gpio::GpioConfig { 290 | mode: gpio::GpioMode::AF, 291 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 292 | otype: gpio::GpioOType::PUSH_PULL, 293 | pupd: gpio::GpioPuPd::PULL_UP, 294 | af: gpio::GpioAF::AF7, 295 | }, 296 | ); 297 | 298 | // The RX and TX pins are now connected to their AF so that the 299 | // USART2 can take over control of the pins 300 | USART2.enable(&usart::UsartConfig { 301 | data_bits: usart::DataBits::Bits8, 302 | stop_bits: usart::StopBits::Bits1, 303 | flow_control: usart::FlowControl::No, 304 | baud_rate: 115_200, 305 | }); 306 | 307 | USART2.it_enable(usart::Interrupt::RXNE); 308 | 309 | nvic::init(&nvic::NvicInit { 310 | irq_channel: nvic::IrqChannel::USART2, 311 | priority: 0, 312 | subpriority: 1, 313 | enable: true, 314 | }); 315 | } 316 | 317 | #[cfg(target_os = "none")] 318 | pub mod panicking { 319 | use core::fmt::Write; 320 | use core::panic::PanicInfo; 321 | use stm32f4::usart::USART2; 322 | 323 | // #[lang = "panic_fmt"] 324 | #[panic_handler] 325 | fn panic(info: &PanicInfo) -> ! { 326 | { 327 | let _lock = unsafe { ::stm32f4::IrqLock::new() }; 328 | match info.location() { 329 | Some(loc) => { 330 | let _ = write!( 331 | unsafe { &USART2 }, 332 | "\r\nPANIC\r\n{}:{}", 333 | loc.file(), 334 | loc.line() 335 | ); 336 | } 337 | None => { 338 | let _ = write!(unsafe { &USART2 }, "\r\nPANIC\r\n"); 339 | } 340 | } 341 | } 342 | loop { 343 | unsafe { ::stm32f4::__wait_for_interrupt() }; 344 | } 345 | } 346 | 347 | #[alloc_error_handler] 348 | fn alloc_error(_: core::alloc::Layout) -> ! { 349 | { 350 | let _lock = unsafe { ::stm32f4::IrqLock::new() }; 351 | let _ = write!(unsafe { &USART2 }, "\r\nALLOC ERROR\r\n"); 352 | } 353 | 354 | loop { 355 | unsafe { ::stm32f4::__wait_for_interrupt() }; 356 | } 357 | } 358 | } 359 | 360 | #[no_mangle] 361 | pub unsafe extern "C" fn __isr_tim2() { 362 | static mut LED3_VALUE: bool = false; 363 | 364 | if TIM2.it_status(timer::Dier::UIE) { 365 | TIM2.it_clear_pending(timer::Dier::UIE); 366 | 367 | LED3_VALUE = !LED3_VALUE; 368 | if LED3_VALUE { 369 | led::LD3.turn_on(); 370 | } else { 371 | led::LD3.turn_off(); 372 | } 373 | } 374 | } 375 | 376 | unsafe fn init_i2c() { 377 | use stm32f4::i2c; 378 | 379 | rcc::RCC.ahb1_clock_enable(rcc::Ahb1Enable::GPIOD); 380 | GPIO_D.enable( 381 | 4, 382 | gpio::GpioConfig { 383 | mode: gpio::GpioMode::OUTPUT, 384 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 385 | otype: gpio::GpioOType::PUSH_PULL, 386 | pupd: gpio::GpioPuPd::PULL_DOWN, 387 | af: gpio::GpioAF::AF0, 388 | }, 389 | ); 390 | 391 | GPIO_D.set_bit(4); 392 | 393 | rcc::RCC.ahb1_clock_enable(rcc::Ahb1Enable::GPIOB); 394 | 395 | GPIO_B.enable( 396 | 6, 397 | gpio::GpioConfig { 398 | mode: gpio::GpioMode::AF, 399 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 400 | otype: gpio::GpioOType::OPEN_DRAIN, 401 | pupd: gpio::GpioPuPd::NO, 402 | af: gpio::GpioAF::AF4, 403 | }, 404 | ); 405 | GPIO_B.enable( 406 | 9, 407 | gpio::GpioConfig { 408 | mode: gpio::GpioMode::AF, 409 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 410 | otype: gpio::GpioOType::OPEN_DRAIN, 411 | pupd: gpio::GpioPuPd::NO, 412 | af: gpio::GpioAF::AF4, 413 | }, 414 | ); 415 | 416 | rcc::RCC.apb1_clock_enable(rcc::Apb1Enable::I2C1); 417 | i2c::I2C1.init(&i2c::I2cInit { 418 | clock_speed: 10000, 419 | mode: i2c::Mode::I2C, 420 | duty_cycle: i2c::DutyCycle::DutyCycle_2, 421 | own_address1: 0, 422 | ack: i2c::Acknowledgement::Disable, 423 | acknowledged_address: i2c::AcknowledgedAddress::Bit7, 424 | }); 425 | 426 | nvic::init(&nvic::NvicInit { 427 | irq_channel: nvic::IrqChannel::I2C1_EV, 428 | priority: 4, 429 | subpriority: 1, 430 | enable: true, 431 | }); 432 | nvic::init(&nvic::NvicInit { 433 | irq_channel: nvic::IrqChannel::I2C1_ER, 434 | priority: 4, 435 | subpriority: 1, 436 | enable: true, 437 | }); 438 | } 439 | 440 | unsafe fn init_rng() { 441 | rcc::RCC.ahb2_clock_enable(rcc::Ahb2Enable::RNG); 442 | 443 | nvic::init(&nvic::NvicInit { 444 | irq_channel: nvic::IrqChannel::HASH_RNG, 445 | priority: 4, 446 | subpriority: 1, 447 | enable: true, 448 | }); 449 | } 450 | 451 | unsafe fn init_esp8266() { 452 | use ::stm32f4::usart::USART3; 453 | 454 | RCC.apb1_clock_enable(rcc::Apb1Enable::USART3); 455 | 456 | // Enable the peripheral clock for the pins used by USART3, PD8 457 | // for TX and PD9 for RX 458 | RCC.ahb1_clock_enable(rcc::Ahb1Enable::GPIOD); 459 | 460 | GPIO_D.enable( 461 | 8, 462 | gpio::GpioConfig { 463 | mode: gpio::GpioMode::AF, 464 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 465 | otype: gpio::GpioOType::PUSH_PULL, 466 | pupd: gpio::GpioPuPd::PULL_UP, 467 | af: gpio::GpioAF::AF7, 468 | }, 469 | ); 470 | GPIO_D.enable( 471 | 9, 472 | gpio::GpioConfig { 473 | mode: gpio::GpioMode::AF, 474 | ospeed: gpio::GpioOSpeed::FAST_SPEED, 475 | otype: gpio::GpioOType::PUSH_PULL, 476 | pupd: gpio::GpioPuPd::PULL_UP, 477 | af: gpio::GpioAF::AF7, 478 | }, 479 | ); 480 | 481 | // The RX and TX pins are now connected to their AF so that the 482 | // USART3 can take over control of the pins 483 | USART3.enable(&usart::UsartConfig { 484 | data_bits: usart::DataBits::Bits8, 485 | stop_bits: usart::StopBits::Bits1, 486 | flow_control: usart::FlowControl::No, 487 | baud_rate: 115_200, 488 | }); 489 | 490 | USART3.it_enable(usart::Interrupt::RXNE); 491 | 492 | nvic::init(&nvic::NvicInit { 493 | irq_channel: nvic::IrqChannel::USART3, 494 | priority: 0, 495 | subpriority: 4, 496 | enable: true, 497 | }); 498 | } 499 | 500 | #[no_mangle] 501 | pub unsafe extern "C" fn __isr_usart2() { 502 | USART2.isr() 503 | } 504 | 505 | #[no_mangle] 506 | pub unsafe extern "C" fn __isr_usart3() { 507 | USART3.isr() 508 | } 509 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | 3 | NOTE: The modification to the GPL is included to allow you to distribute a 4 | combined work that includes bkernel without being obliged to provide the source 5 | code for proprietary components. 6 | 7 | ---------------------------------------------------------------------------- 8 | 9 | The bkernel GPL Exception Text follows: 10 | 11 | Any bkernel *source code*, whether modified or in it's original release form, 12 | or whether in whole or in part, can only be distributed by you under the terms 13 | of the GNU General Public License plus this exception. An independent module is 14 | a module which is not derived from or based on bkernel. 15 | 16 | Clause 1: 17 | 18 | Linking bkernel with other modules is making a combined work based on bkernel. 19 | Thus, the terms and conditions of the GNU General Public License V2 cover the 20 | whole combination. 21 | 22 | As a special exception, the copyright holders of bkernel give you permission to 23 | link bkernel with independent modules to produce a statically linked 24 | executable, regardless of the license terms of these independent modules, and to 25 | copy and distribute the resulting executable under terms of your choice, 26 | provided that you also meet, for each linked independent module, the terms and 27 | conditions of the license of that module. An independent module is a module 28 | which is not derived from or based on bkernel. 29 | 30 | 31 | -------------------------------------------------------------------- 32 | 33 | 34 | 35 | The standard GPL V2 text: 36 | 37 | 38 | GNU GENERAL PUBLIC LICENSE 39 | Version 2, June 1991 40 | 41 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 42 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 43 | Everyone is permitted to copy and distribute verbatim copies 44 | of this license document, but changing it is not allowed. 45 | 46 | Preamble 47 | 48 | The licenses for most software are designed to take away your 49 | freedom to share and change it. By contrast, the GNU General Public 50 | License is intended to guarantee your freedom to share and change free 51 | software--to make sure the software is free for all its users. This 52 | General Public License applies to most of the Free Software 53 | Foundation's software and to any other program whose authors commit to 54 | using it. (Some other Free Software Foundation software is covered by 55 | the GNU Library General Public License instead.) You can apply it to 56 | your programs, too. 57 | 58 | When we speak of free software, we are referring to freedom, not 59 | price. Our General Public Licenses are designed to make sure that you 60 | have the freedom to distribute copies of free software (and charge for 61 | this service if you wish), that you receive source code or can get it 62 | if you want it, that you can change the software or use pieces of it 63 | in new free programs; and that you know you can do these things. 64 | 65 | To protect your rights, we need to make restrictions that forbid 66 | anyone to deny you these rights or to ask you to surrender the rights. 67 | These restrictions translate to certain responsibilities for you if you 68 | distribute copies of the software, or if you modify it. 69 | 70 | For example, if you distribute copies of such a program, whether 71 | gratis or for a fee, you must give the recipients all the rights that 72 | you have. You must make sure that they, too, receive or can get the 73 | source code. And you must show them these terms so they know their 74 | rights. 75 | 76 | We protect your rights with two steps: (1) copyright the software, and 77 | (2) offer you this license which gives you legal permission to copy, 78 | distribute and/or modify the software. 79 | 80 | Also, for each author's protection and ours, we want to make certain 81 | that everyone understands that there is no warranty for this free 82 | software. If the software is modified by someone else and passed on, we 83 | want its recipients to know that what they have is not the original, so 84 | that any problems introduced by others will not reflect on the original 85 | authors' reputations. 86 | 87 | Finally, any free program is threatened constantly by software 88 | patents. We wish to avoid the danger that redistributors of a free 89 | program will individually obtain patent licenses, in effect making the 90 | program proprietary. To prevent this, we have made it clear that any 91 | patent must be licensed for everyone's free use or not licensed at all. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. 95 | 96 | GNU GENERAL PUBLIC LICENSE 97 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 98 | 99 | 0. This License applies to any program or other work which contains 100 | a notice placed by the copyright holder saying it may be distributed 101 | under the terms of this General Public License. The "Program", below, 102 | refers to any such program or work, and a "work based on the Program" 103 | means either the Program or any derivative work under copyright law: 104 | that is to say, a work containing the Program or a portion of it, 105 | either verbatim or with modifications and/or translated into another 106 | language. (Hereinafter, translation is included without limitation in 107 | the term "modification".) Each licensee is addressed as "you". 108 | 109 | Activities other than copying, distribution and modification are not 110 | covered by this License; they are outside its scope. The act of 111 | running the Program is not restricted, and the output from the Program 112 | is covered only if its contents constitute a work based on the 113 | Program (independent of having been made by running the Program). 114 | Whether that is true depends on what the Program does. 115 | 116 | 1. You may copy and distribute verbatim copies of the Program's 117 | source code as you receive it, in any medium, provided that you 118 | conspicuously and appropriately publish on each copy an appropriate 119 | copyright notice and disclaimer of warranty; keep intact all the 120 | notices that refer to this License and to the absence of any warranty; 121 | and give any other recipients of the Program a copy of this License 122 | along with the Program. 123 | 124 | You may charge a fee for the physical act of transferring a copy, and 125 | you may at your option offer warranty protection in exchange for a fee. 126 | 127 | 2. You may modify your copy or copies of the Program or any portion 128 | of it, thus forming a work based on the Program, and copy and 129 | distribute such modifications or work under the terms of Section 1 130 | above, provided that you also meet all of these conditions: 131 | 132 | a) You must cause the modified files to carry prominent notices 133 | stating that you changed the files and the date of any change. 134 | 135 | b) You must cause any work that you distribute or publish, that in 136 | whole or in part contains or is derived from the Program or any 137 | part thereof, to be licensed as a whole at no charge to all third 138 | parties under the terms of this License. 139 | 140 | c) If the modified program normally reads commands interactively 141 | when run, you must cause it, when started running for such 142 | interactive use in the most ordinary way, to print or display an 143 | announcement including an appropriate copyright notice and a 144 | notice that there is no warranty (or else, saying that you provide 145 | a warranty) and that users may redistribute the program under 146 | these conditions, and telling the user how to view a copy of this 147 | License. (Exception: if the Program itself is interactive but 148 | does not normally print such an announcement, your work based on 149 | the Program is not required to print an announcement.) 150 | 151 | These requirements apply to the modified work as a whole. If 152 | identifiable sections of that work are not derived from the Program, 153 | and can be reasonably considered independent and separate works in 154 | themselves, then this License, and its terms, do not apply to those 155 | sections when you distribute them as separate works. But when you 156 | distribute the same sections as part of a whole which is a work based 157 | on the Program, the distribution of the whole must be on the terms of 158 | this License, whose permissions for other licensees extend to the 159 | entire whole, and thus to each and every part regardless of who wrote it. 160 | 161 | Thus, it is not the intent of this section to claim rights or contest 162 | your rights to work written entirely by you; rather, the intent is to 163 | exercise the right to control the distribution of derivative or 164 | collective works based on the Program. 165 | 166 | In addition, mere aggregation of another work not based on the Program 167 | with the Program (or with a work based on the Program) on a volume of 168 | a storage or distribution medium does not bring the other work under 169 | the scope of this License. 170 | 171 | 3. You may copy and distribute the Program (or a work based on it, 172 | under Section 2) in object code or executable form under the terms of 173 | Sections 1 and 2 above provided that you also do one of the following: 174 | 175 | a) Accompany it with the complete corresponding machine-readable 176 | source code, which must be distributed under the terms of Sections 177 | 1 and 2 above on a medium customarily used for software interchange; or, 178 | 179 | b) Accompany it with a written offer, valid for at least three 180 | years, to give any third party, for a charge no more than your 181 | cost of physically performing source distribution, a complete 182 | machine-readable copy of the corresponding source code, to be 183 | distributed under the terms of Sections 1 and 2 above on a medium 184 | customarily used for software interchange; or, 185 | 186 | c) Accompany it with the information you received as to the offer 187 | to distribute corresponding source code. (This alternative is 188 | allowed only for noncommercial distribution and only if you 189 | received the program in object code or executable form with such 190 | an offer, in accord with Subsection b above.) 191 | 192 | The source code for a work means the preferred form of the work for 193 | making modifications to it. For an executable work, complete source 194 | code means all the source code for all modules it contains, plus any 195 | associated interface definition files, plus the scripts used to 196 | control compilation and installation of the executable. However, as a 197 | special exception, the source code distributed need not include 198 | anything that is normally distributed (in either source or binary 199 | form) with the major components (compiler, kernel, and so on) of the 200 | operating system on which the executable runs, unless that component 201 | itself accompanies the executable. 202 | 203 | If distribution of executable or object code is made by offering 204 | access to copy from a designated place, then offering equivalent 205 | access to copy the source code from the same place counts as 206 | distribution of the source code, even though third parties are not 207 | compelled to copy the source along with the object code. 208 | 209 | 4. You may not copy, modify, sublicense, or distribute the Program 210 | except as expressly provided under this License. Any attempt 211 | otherwise to copy, modify, sublicense or distribute the Program is 212 | void, and will automatically terminate your rights under this License. 213 | However, parties who have received copies, or rights, from you under 214 | this License will not have their licenses terminated so long as such 215 | parties remain in full compliance. 216 | 217 | 5. You are not required to accept this License, since you have not 218 | signed it. However, nothing else grants you permission to modify or 219 | distribute the Program or its derivative works. These actions are 220 | prohibited by law if you do not accept this License. Therefore, by 221 | modifying or distributing the Program (or any work based on the 222 | Program), you indicate your acceptance of this License to do so, and 223 | all its terms and conditions for copying, distributing or modifying 224 | the Program or works based on it. 225 | 226 | 6. Each time you redistribute the Program (or any work based on the 227 | Program), the recipient automatically receives a license from the 228 | original licensor to copy, distribute or modify the Program subject to 229 | these terms and conditions. You may not impose any further 230 | restrictions on the recipients' exercise of the rights granted herein. 231 | You are not responsible for enforcing compliance by third parties to 232 | this License. 233 | 234 | 7. If, as a consequence of a court judgment or allegation of patent 235 | infringement or for any other reason (not limited to patent issues), 236 | conditions are imposed on you (whether by court order, agreement or 237 | otherwise) that contradict the conditions of this License, they do not 238 | excuse you from the conditions of this License. If you cannot 239 | distribute so as to satisfy simultaneously your obligations under this 240 | License and any other pertinent obligations, then as a consequence you 241 | may not distribute the Program at all. For example, if a patent 242 | license would not permit royalty-free redistribution of the Program by 243 | all those who receive copies directly or indirectly through you, then 244 | the only way you could satisfy both it and this License would be to 245 | refrain entirely from distribution of the Program. 246 | 247 | If any portion of this section is held invalid or unenforceable under 248 | any particular circumstance, the balance of the section is intended to 249 | apply and the section as a whole is intended to apply in other 250 | circumstances. 251 | 252 | It is not the purpose of this section to induce you to infringe any 253 | patents or other property right claims or to contest validity of any 254 | such claims; this section has the sole purpose of protecting the 255 | integrity of the free software distribution system, which is 256 | implemented by public license practices. Many people have made 257 | generous contributions to the wide range of software distributed 258 | through that system in reliance on consistent application of that 259 | system; it is up to the author/donor to decide if he or she is willing 260 | to distribute software through any other system and a licensee cannot 261 | impose that choice. 262 | 263 | This section is intended to make thoroughly clear what is believed to 264 | be a consequence of the rest of this License. 265 | 266 | 8. If the distribution and/or use of the Program is restricted in 267 | certain countries either by patents or by copyrighted interfaces, the 268 | original copyright holder who places the Program under this License 269 | may add an explicit geographical distribution limitation excluding 270 | those countries, so that distribution is permitted only in or among 271 | countries not thus excluded. In such case, this License incorporates 272 | the limitation as if written in the body of this License. 273 | 274 | 9. The Free Software Foundation may publish revised and/or new versions 275 | of the General Public License from time to time. Such new versions will 276 | be similar in spirit to the present version, but may differ in detail to 277 | address new problems or concerns. 278 | 279 | Each version is given a distinguishing version number. If the Program 280 | specifies a version number of this License which applies to it and "any 281 | later version", you have the option of following the terms and conditions 282 | either of that version or of any later version published by the Free 283 | Software Foundation. If the Program does not specify a version number of 284 | this License, you may choose any version ever published by the Free Software 285 | Foundation. 286 | 287 | 10. If you wish to incorporate parts of the Program into other free 288 | programs whose distribution conditions are different, write to the author 289 | to ask for permission. For software which is copyrighted by the Free 290 | Software Foundation, write to the Free Software Foundation; we sometimes 291 | make exceptions for this. Our decision will be guided by the two goals 292 | of preserving the free status of all derivatives of our free software and 293 | of promoting the sharing and reuse of software generally. 294 | 295 | NO WARRANTY 296 | 297 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 298 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 299 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 300 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 301 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 302 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 303 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 304 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 305 | REPAIR OR CORRECTION. 306 | 307 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 308 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 309 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 310 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 311 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 312 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 313 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 314 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 315 | POSSIBILITY OF SUCH DAMAGES. 316 | 317 | END OF TERMS AND CONDITIONS 318 | 319 | How to Apply These Terms to Your New Programs 320 | 321 | If you develop a new program, and you want it to be of the greatest 322 | possible use to the public, the best way to achieve this is to make it 323 | free software which everyone can redistribute and change under these terms. 324 | 325 | To do so, attach the following notices to the program. It is safest 326 | to attach them to the start of each source file to most effectively 327 | convey the exclusion of warranty; and each file should have at least 328 | the "copyright" line and a pointer to where the full notice is found. 329 | 330 | 331 | Copyright (C) 332 | 333 | This program is free software; you can redistribute it and/or modify 334 | it under the terms of the GNU General Public License** as published by 335 | the Free Software Foundation; either version 2 of the License, or 336 | (at your option) any later version. 337 | 338 | This program is distributed in the hope that it will be useful, 339 | but WITHOUT ANY WARRANTY; without even the implied warranty of 340 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 341 | GNU General Public License for more details. 342 | 343 | You should have received a copy of the GNU General Public License 344 | along with this program; if not, write to the Free Software 345 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 346 | 347 | 348 | Also add information on how to contact you by electronic and paper mail. 349 | 350 | If the program is interactive, make it output a short notice like this 351 | when it starts in an interactive mode: 352 | 353 | Gnomovision version 69, Copyright (C) year name of author 354 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 355 | This is free software, and you are welcome to redistribute it 356 | under certain conditions; type `show c' for details. 357 | 358 | The hypothetical commands `show w' and `show c' should show the appropriate 359 | parts of the General Public License. Of course, the commands you use may 360 | be called something other than `show w' and `show c'; they could even be 361 | mouse-clicks or menu items--whatever suits your program. 362 | 363 | You should also get your employer (if you work as a programmer) or your 364 | school, if any, to sign a "copyright disclaimer" for the program, if 365 | necessary. Here is a sample; alter the names: 366 | 367 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 368 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 369 | 370 | , 1 April 1989 371 | Ty Coon, President of Vice 372 | 373 | This General Public License does not permit incorporating your program into 374 | proprietary programs. If your program is a subroutine library, you may 375 | consider it more useful to permit linking proprietary applications with the 376 | library. If this is what you want to do, use the GNU Library General 377 | Public License instead of this License. 378 | --------------------------------------------------------------------------------