neotron_common_bios/lib.rs
1//! # Neotron Common BIOS
2//!
3//! Contains the common API for all Neotron BIOS implementations.
4//!
5//! ## License
6//!
7//! > Copyright (C) The Neotron Developers, 2019-2022
8//! >
9//! > This program is free software: you can redistribute it and/or modify
10//! > it under the terms of the GNU General Public License as published by
11//! > the Free Software Foundation, either version 3 of the License, or
12//! > at your option) any later version.
13//! >
14//! > This program is distributed in the hope that it will be useful,
15//! > but WITHOUT ANY WARRANTY; without even the implied warranty of
16//! > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17//! > GNU General Public License for more details.
18//! >
19//! > You should have received a copy of the GNU General Public License
20//! > along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22#![no_std]
23#![deny(missing_docs)]
24
25// ============================================================================
26// Imports
27// ============================================================================
28
29pub mod audio;
30pub mod block_dev;
31pub mod bus;
32pub mod hid;
33pub mod i2c;
34pub mod serial;
35pub mod types;
36pub mod version;
37pub mod video;
38
39pub use types::*;
40pub use version::Version;
41
42pub use neotron_ffi::{FfiBuffer, FfiByteSlice, FfiOption, FfiResult, FfiString};
43
44// ============================================================================
45// Constants
46// ============================================================================
47
48/// BIOS API semantic version for the API defined in this crate.
49pub const API_VERSION: Version = Version::new(0, 6, 1);
50
51// ============================================================================
52// Macros
53// ============================================================================
54
55/// Creates an FFI-safe struct to use in place of an enum.
56#[macro_export]
57macro_rules! make_ffi_enum {
58 (
59 $enumdoc: literal,
60 $enum_name:ident,
61 $ffi_enum_name:ident,
62 {
63 $(
64 $(
65 #[doc = $docs:literal]
66 )+
67 $variant:ident
68 ),+
69 }
70 ) => {
71 #[doc = $enumdoc]
72 ///
73 /// Can be converted to [
74 #[doc = stringify!($ffi_enum_name)]
75 /// ] for transport across an FFI boundary.
76 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
77 #[non_exhaustive]
78 #[repr(u8)]
79 pub enum $enum_name {
80 $(
81 $(
82 #[doc = $docs]
83 )+
84 $variant
85 ),+
86 }
87
88 impl $enum_name {
89 /// Convert this enum into an FFI-safe [
90 #[doc = stringify!($ffi_enum_name)]
91 /// ] value.
92 pub const fn make_ffi_safe(self) -> $ffi_enum_name {
93 $ffi_enum_name::new(self)
94 }
95 }
96
97 /// An FFI-safe version of [
98 #[doc = stringify!($enum_name)]
99 /// ]
100 #[repr(transparent)]
101 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
102 pub struct $ffi_enum_name(pub u8);
103
104 impl $ffi_enum_name {
105 /// Create an FFI-safe version of [
106 #[doc = stringify!($enum_name)]
107 /// ]
108 pub const fn new(value: $enum_name) -> Self {
109 Self(value as u8)
110 }
111
112 /// Try and convert from this FFI-safe value to a real enum.
113 pub const fn make_safe(self) -> Result<$enum_name, $crate::EnumConversionFail> {
114 $(
115 if self.0 == ($enum_name::$variant as u8) {
116 return Ok($enum_name::$variant);
117 }
118 )+
119 Err($crate::EnumConversionFail())
120 }
121 }
122
123 impl ::core::convert::From<$enum_name> for $ffi_enum_name {
124 /// Convert from the enum to the FFI-safe version.
125 ///
126 /// This conversion is infallible.
127 fn from(value: $enum_name) -> Self {
128 value.make_ffi_safe()
129 }
130 }
131
132 impl ::core::convert::TryFrom<$ffi_enum_name> for $enum_name {
133 type Error = $crate::EnumConversionFail;
134
135 /// Try and convert the FFI-safe version into an enum.
136 ///
137 /// Might fail if the other side of the FFI boundary knows about
138 /// enum variants we don't yet know about.
139 fn try_from(value: $ffi_enum_name) -> Result<Self, $crate::EnumConversionFail> {
140 value.make_safe()
141 }
142 }
143 }
144}
145
146// ============================================================================
147// Types
148// ============================================================================
149
150/// Describes the result of an API call.
151///
152/// It's an FFI-safe Result [`FfiResult`], but the error type is fixed to be
153/// [`Error`].
154pub type ApiResult<T> = neotron_ffi::FfiResult<T, Error>;
155
156/// The BIOS API, expressed as a structure of function pointers.
157///
158/// All Neotron BIOSes should provide this structure to the OS initialisation
159/// function.
160#[repr(C)]
161pub struct Api {
162 // ========================================================================
163 // Version and Metadata
164 // ========================================================================
165 /// Gets the version number of the BIOS API.
166 ///
167 /// You need this value to determine which of the following API calls are
168 /// valid in this particular version.
169 pub api_version_get: extern "C" fn() -> Version,
170 /// Returns a pointer to a static string slice.
171 ///
172 /// This string contains the version number and build string of the BIOS.
173 /// For C compatibility this string is null-terminated and guaranteed to
174 /// only contain ASCII characters (bytes with a value 127 or lower). We
175 /// also pass the length (excluding the null) to make it easy to construct
176 /// a Rust string. It is unspecified as to whether the string is located
177 /// in Flash ROM or RAM (but it's likely to be Flash ROM).
178 pub bios_version_get: extern "C" fn() -> FfiString<'static>,
179
180 // ========================================================================
181 // Serial Port Support
182 // ========================================================================
183 /// Get information about the Serial ports in the system.
184 ///
185 /// Serial ports are ordered octet-oriented pipes. You can push octets
186 /// into them using a 'write' call, and pull bytes out of them using a
187 /// 'read' call. They have options which allow them to be configured at
188 /// different speeds, or with different transmission settings (parity
189 /// bits, stop bits, etc) - you set these with a call to
190 /// `SerialConfigure`. They may physically be a MIDI interface, an RS-232
191 /// port or a USB-Serial port. There is no sense of 'open' or 'close' -
192 /// that is an Operating System level design feature. These APIs just
193 /// reflect the raw hardware, in a similar manner to the registers exposed
194 /// by a memory-mapped UART peripheral.
195 pub serial_get_info: extern "C" fn(device_id: u8) -> crate::FfiOption<serial::DeviceInfo>,
196 /// Set the options for a given serial device. An error is returned if the
197 /// options are invalid for that serial device.
198 pub serial_configure:
199 extern "C" fn(device_id: u8, config: serial::Config) -> crate::ApiResult<()>,
200 /// Write bytes to a serial port. There is no sense of 'opening' or
201 /// 'closing' the device - serial devices are always open. If the return
202 /// value is `Ok(n)`, the value `n` may be less than the size of the given
203 /// buffer. If so, that means not all of the data could be transmitted -
204 /// only the first `n` bytes were.
205 pub serial_write: extern "C" fn(
206 device_id: u8,
207 data: FfiByteSlice,
208 timeout: crate::FfiOption<Timeout>,
209 ) -> crate::ApiResult<usize>,
210 /// Read bytes from a serial port. There is no sense of 'opening'
211 /// or 'closing' the device - serial devices are always open. If the
212 /// return value is `Ok(n)`, the value `n` may be less than the size of
213 /// the given buffer. If so, that means not all of the requested data
214 /// could be received - only the first `n` bytes were (and hence only the
215 /// first `n` bytes of the given buffer now contain data).
216 pub serial_read: extern "C" fn(
217 device_id: u8,
218 data: FfiBuffer,
219 timeout: crate::FfiOption<Timeout>,
220 ) -> crate::ApiResult<usize>,
221
222 // ========================================================================
223 // Time Support
224 // ========================================================================
225 /// Get the current wall time.
226 ///
227 /// The Neotron BIOS does not understand time zones, leap-seconds or the
228 /// Gregorian calendar. It simply stores time as an incrementing number of
229 /// seconds since some epoch, and the number of video frames (at 60 Hz)
230 /// since that second began. A day is assumed to be exactly 86,400 seconds
231 /// long. This is a lot like POSIX time, except we have a different epoch
232 /// - the Neotron epoch is 2000-01-01T00:00:00Z. It is highly recommend
233 /// that you store UTC in the BIOS and use the OS to handle time-zones.
234 ///
235 /// If the BIOS does not have a battery-backed clock, or if that battery
236 /// has failed to keep time, the system starts up assuming it is the
237 /// epoch.
238 pub time_clock_get: extern "C" fn() -> Time,
239 /// Set the current wall time.
240 ///
241 /// See `time_get` for a description of now the Neotron BIOS should handle
242 /// time.
243 ///
244 /// You only need to call this whenever you get a new sense of the current
245 /// time (e.g. the user has updated the current time, or if you get a GPS
246 /// fix). The BIOS should push the time out to the battery-backed Real
247 /// Time Clock, if it has one.
248 pub time_clock_set: extern "C" fn(time: Time),
249 /// Get the current monotonic system time.
250 ///
251 /// This value will never go backwards and it should never wrap.
252 pub time_ticks_get: extern "C" fn() -> Ticks,
253 /// Report the system tick rate, in ticks-per-second.
254 pub time_ticks_per_second: extern "C" fn() -> Ticks,
255
256 // ========================================================================
257 // Persistent Configuration Support
258 // ========================================================================
259 /// Get the configuration data block.
260 ///
261 /// Configuration data is, to the BIOS, just a block of bytes of a given
262 /// length. How it stores them is up to the BIOS - it could be EEPROM, or
263 /// battery-backed SRAM.
264 pub configuration_get: extern "C" fn(buffer: FfiBuffer) -> crate::ApiResult<usize>,
265 /// Set the configuration data block.
266 ///
267 /// See `configuration_get`.
268 pub configuration_set: extern "C" fn(buffer: FfiByteSlice) -> crate::ApiResult<()>,
269
270 // ========================================================================
271 // Video Output Support
272 // ========================================================================
273 /// Does this Neotron BIOS support this video mode?
274 pub video_is_valid_mode: extern "C" fn(mode: video::Mode) -> bool,
275 /// Does this Neotron BIOS require extra VRAM for this mode to work?
276 ///
277 /// If `true` returned here, you must pass some VRAM in the call to
278 /// [`Api::video_set_mode`], otherwise that function will return an error.
279 ///
280 /// If `false` returned here, you can pass NULL to [`Api::video_set_mode`].
281 pub video_mode_needs_vram: extern "C" fn(mode: video::Mode) -> bool,
282 /// Switch to a new video mode, passing an optional pointer to some VRAM.
283 ///
284 /// If the `vram` pointer is NULL, the BIOS will attempt to use any internal
285 /// VRAM it has available. If it doesn't have enough VRAM, you will get no
286 /// picture.
287 ///
288 /// # Safety
289 ///
290 /// If a non-null `vram` value is given, it must be the start of a 32-bit
291 /// aligned block which is at least [`frame_size_bytes()`](
292 /// video::Mode::frame_size_bytes) bytes in length
293 pub video_set_mode:
294 unsafe extern "C" fn(mode: video::Mode, vram: *mut u32) -> crate::ApiResult<()>,
295 /// Returns the video mode the BIOS is currently in.
296 ///
297 /// The OS should call this function immediately after start-up and note
298 /// the value - this is the `default` video mode which can always be
299 /// serviced without supplying extra RAM.
300 pub video_get_mode: extern "C" fn() -> video::Mode,
301 /// Get the framebuffer address.
302 ///
303 /// We can write through this address to the video framebuffer. The
304 /// meaning of the data we write, and the size of the region we are
305 /// allowed to write to, is a function of the current video mode (see
306 /// `video_get_mode`).
307 ///
308 /// This function will return `null` if the BIOS isn't able to support the
309 /// current video mode from its memory reserves. If that happens, you will
310 /// need to use some OS RAM or Application RAM and provide that as a
311 /// framebuffer to `video_set_framebuffer`. The BIOS will always be able
312 /// to provide the 'basic' text buffer experience from reserves, so this
313 /// function will never return `null` on start-up.
314 pub video_get_framebuffer: extern "C" fn() -> *mut u32,
315 /// Wait for the next occurence of the specified video scan-line.
316 ///
317 /// In general we must assume that the video memory is read top-to-bottom
318 /// as the picture is being drawn on the monitor (e.g. via a VGA video
319 /// signal). If you modify video memory during this *drawing period*
320 /// there is a risk that the image on the monitor (however briefly) may
321 /// contain some parts from before the modification and some parts from
322 /// after. This can given rise to the *tearing effect* where it looks
323 /// like the screen has been torn (or ripped) across because there is a
324 /// discontinuity part-way through the image.
325 ///
326 /// This function busy-waits until the video drawing has reached a
327 /// specified scan-line on the video frame.
328 ///
329 /// There is no error code here. If the line you ask for is beyond the
330 /// number of visible scan-lines in the current video mode, it waits util
331 /// the last visible scan-line is complete.
332 ///
333 /// If you wait for the last visible line until drawing, you stand the
334 /// best chance of your pixels operations on the video RAM being
335 /// completed before scan-lines start being sent to the monitor for the
336 /// next frame.
337 ///
338 /// You can also use this for a crude `16.7 ms` delay but note that
339 /// some video modes run at `70 Hz` and so this would then give you a
340 /// `14.3ms` second delay.
341 pub video_wait_for_line: extern "C" fn(line: u16),
342 /// Get an entry from the colour palette.
343 ///
344 /// Almost all video modes (except `Chunky16` and `Chunky32`) use a video
345 /// palette. This function returns the RGB colour for a given palette
346 /// index.
347 ///
348 /// If you ask for an entry that is beyond the capabilities of the current
349 /// video mode, you get `None`.
350 pub video_get_palette: extern "C" fn(palette_idx: u8) -> crate::FfiOption<video::RGBColour>,
351 /// Set an entry in the colour palette.
352 ///
353 /// Almost all video modes (except `Chunky16` and `Chunky32`) use a video
354 /// palette. This function changes the RGB colour for a given palette
355 /// index.
356 ///
357 /// If you set an entry beyond what the current mode supports, the value
358 /// is ignored.
359 pub video_set_palette: extern "C" fn(palette_idx: u8, video::RGBColour),
360 /// Sets all the entries in the colour palette at once.
361 ///
362 /// Almost all video modes (except `Chunky16` and `Chunky32`) use a video
363 /// palette. This function changes all the RGB colours in the current
364 /// palette.
365 ///
366 /// If you pass a `len` beyond what the current mode supports, the extra
367 /// values are ignored. The given buffer is copied so it doesn't need to
368 /// live beyond this function call.
369 ///
370 /// # Safety
371 ///
372 /// The value `start` must point to an array of `RGBColour` of length
373 /// `length`.
374 ///
375 pub video_set_whole_palette:
376 unsafe extern "C" fn(start: *const video::RGBColour, length: usize),
377
378 // ========================================================================
379 // Memory Region Support
380 // ========================================================================
381 /// Find out about regions of memory in the system.
382 ///
383 /// The first region (index `0`) must be the 'application region' which is
384 /// defined to always start at address `0x2000_0400` (that is, 1 KiB into
385 /// main SRAM) on a standard Cortex-M system. This application region stops
386 /// just before the BIOS reserved memory, typically at the top of the
387 /// internal SRAM.
388 ///
389 /// Other regions may be located at other addresses (e.g. external DRAM or
390 /// PSRAM).
391 ///
392 /// The OS will always load non-relocatable applications into the bottom of
393 /// Region 0. It can allocate OS specific structures from any other Region
394 /// (if any), or from the top of Region 0 (although this reduces the maximum
395 /// application space available). The OS will prefer lower numbered regions
396 /// (other than Region 0), so faster memory should be listed first.
397 pub memory_get_region: extern "C" fn(region_index: u8) -> crate::FfiOption<MemoryRegion>,
398
399 // ========================================================================
400 // Human Interface Device Support
401 // ========================================================================
402 /// Get the next available HID event, if any.
403 ///
404 /// This function doesn't block. It will return `Ok(None)` if there is no event ready.
405 pub hid_get_event: extern "C" fn() -> crate::ApiResult<crate::FfiOption<hid::HidEvent>>,
406 /// Control the keyboard LEDs.
407 pub hid_set_leds: extern "C" fn(leds: hid::KeyboardLeds) -> crate::ApiResult<()>,
408
409 // ========================================================================
410 // I²C Bus Support
411 // ========================================================================
412 /// Get information about the I²C Buses in the system.
413 ///
414 /// I²C Bus 0 should be the one connected to the Neotron Bus.
415 /// I²C Bus 1 is typically the VGA DDC bus.
416 pub i2c_bus_get_info: extern "C" fn(bus_id: u8) -> crate::FfiOption<i2c::BusInfo>,
417 /// Transact with a I²C Device on an I²C Bus
418 ///
419 /// * `i2c_bus` - Which I²C Bus to use
420 /// * `i2c_device_address` - The 7-bit I²C Device Address
421 /// * `tx` - the first list of bytes to send (use `FfiByteSlice::empty()` if not required)
422 /// * `tx2` - the second (and optional) list of bytes to send (use `FfiByteSlice::empty()` if not required)
423 /// * `rx` - the buffer to fill with read data (use `FfiBuffer::empty()` if not required)
424 ///
425 /// ```no_run
426 /// # let api = neotron_common_bios::Api::make_dummy_api().unwrap();
427 /// # use neotron_common_bios::{FfiByteSlice, FfiBuffer};
428 /// // Read 16 bytes from the start of an EEPROM with device address 0x65 on Bus 0
429 /// let mut buf = [0u8; 16];
430 /// let _ = (api.i2c_write_read)(0, 0x65, FfiByteSlice::new(&[0x00, 0x00]), FfiByteSlice::empty(), FfiBuffer::new(&mut buf));
431 /// // Write those bytes to somewhere else in an EEPROM with device address 0x65 on Bus 0
432 /// // You can see now why it's useful to have *two* TX buffers available
433 /// let _ = (api.i2c_write_read)(0, 0x65, FfiByteSlice::new(&[0x00, 0x10]), FfiByteSlice::new(&buf), FfiBuffer::empty());
434 /// # Ok::<(), neotron_common_bios::Error>(())
435 /// ```
436 pub i2c_write_read: extern "C" fn(
437 bus_id: u8,
438 i2c_device_address: u8,
439 tx: FfiByteSlice,
440 tx2: FfiByteSlice,
441 rx: FfiBuffer,
442 ) -> crate::ApiResult<()>,
443
444 // ========================================================================
445 // Audio Support
446 // ========================================================================
447 /// Get information about the Audio Mixer channels
448 pub audio_mixer_channel_get_info:
449 extern "C" fn(audio_mixer_id: u8) -> crate::FfiOption<audio::MixerChannelInfo>,
450 /// Set an Audio Mixer level
451 pub audio_mixer_channel_set_level:
452 extern "C" fn(audio_mixer_id: u8, level: u8) -> crate::ApiResult<()>,
453 /// Configure the audio output.
454 ///
455 /// If accepted, the audio output FIFO is flushed and the changes apply
456 /// immediately. If not accepted, an error is returned.
457 ///
458 /// It is not currently possible to enumerate all the possible sample
459 /// rates - you just have to try a variety of well know configurations to
460 /// see which ones work.
461 ///
462 /// Note that if your desired sample rate cannot be exactly accepted, but
463 /// is within some tolerance, this function will still succeed. Therefore
464 /// you should call `audio_output_get_config` to get the precise sample
465 /// rate that the system is actually using if that matters to your
466 /// application. For example, you might ask for 48,000 Hz but due to the
467 /// system clock frequency and other factors, a sample rate of 48,018 Hz
468 /// might actually be achieved. Regardless, to avoid buffer underflows
469 /// you should supply as many samples as `audio_output_get_space` says
470 /// you need, not what you think you need based on the sample rate you
471 /// think you have.
472 pub audio_output_set_config: extern "C" fn(config: audio::Config) -> crate::ApiResult<()>,
473 /// Get the audio output's current configuration.
474 pub audio_output_get_config: extern "C" fn() -> crate::ApiResult<audio::Config>,
475 /// Send audio samples to the output FIFO.
476 ///
477 /// The format of the samples (little-endian, 16-bit, etc), depends on the
478 /// current output configuration. Note that the slice is in *bytes* and
479 /// there will be between *one* and *four* bytes per sample depending on
480 /// the format.
481 ///
482 /// This function won't block, but it will return how much data was
483 /// accepted. The given samples will be copied and so the buffer is free
484 /// to re-use once the function returns. To avoid buffer underflows you
485 /// should supply as many samples as `audio_output_get_space` says you
486 /// need, not what you think you need based on the sample rate you think
487 /// you have (as there will always be some error margin on that).
488 ///
489 /// If the buffer underflows, silence is played out.
490 ///
491 /// There is only one hardware output stream so any mixing has to be
492 /// performed in software by the OS.
493 pub audio_output_data: unsafe extern "C" fn(samples: FfiByteSlice) -> crate::ApiResult<usize>,
494 /// Get audio buffer space.
495 ///
496 /// How many samples in the current format can be sent to
497 /// `audio_output_data` without blocking?
498 pub audio_output_get_space: extern "C" fn() -> crate::ApiResult<usize>,
499 /// Configure the audio input.
500 ///
501 /// If accepted, the audio input FIFO is flushed and the changes apply
502 /// immediately. If not accepted, an error is returned.
503 ///
504 /// It is not currently possible to enumerate all the possible sample
505 /// rates - you just have to try a variety of well know configurations to
506 /// see which ones work.
507 ///
508 /// Note that if your desired sample rate cannot be exactly accepted, but
509 /// is within some tolerance, this function will still succeed. Therefore
510 /// you should call `audio_output_get_config` to get the precise sample
511 /// rate that the system is actually using if that matters to your
512 /// application. For example, you might ask for 48,000 Hz but due to the
513 /// system clock frequency and other factors, a sample rate of 48,018 Hz
514 /// might actually be achieved.
515 pub audio_input_set_config: extern "C" fn(config: audio::Config) -> crate::ApiResult<()>,
516 /// Get the audio input's current configuration.
517 pub audio_input_get_config: extern "C" fn() -> crate::ApiResult<audio::Config>,
518 /// Get 16-bit stereo audio from the input FIFO.
519 ///
520 /// The format of the samples (little-endian, 16-bit, etc), depends on the
521 /// current output configuration. Note that the slice is in *bytes* and
522 /// there will be between *one* and *four* bytes per sample depending on
523 /// the format.
524 ///
525 /// This function won't block, but it will return how much data was
526 /// actually written to the buffer.
527 ///
528 /// If you don't call it often enough, there will be a buffer overflow and
529 /// audio will be dropped.
530 pub audio_input_data: unsafe extern "C" fn(samples: FfiBuffer) -> crate::ApiResult<usize>,
531 /// Get audio buffer space.
532 ///
533 /// How many samples in the current format can be read right now using
534 /// `audio_input_data`?
535 pub audio_input_get_count: extern "C" fn() -> crate::ApiResult<usize>,
536
537 // ========================================================================
538 // Neotron (SPI) Bus Support
539 // ========================================================================
540 /// Select a Neotron Bus Peripheral. This drives the SPI chip-select line
541 /// low for that peripheral. Selecting a peripheral de-selects any other
542 /// peripherals. Select peripheral 'None' to select no peripherals. If
543 /// you lock the bus then interrupt routines that need the bus are
544 /// blocked and must be deferred. Therefore you should try and release
545 /// the bus whilst waiting for things to happen (if your peripheral can
546 /// tolerate the CS line being de-activated at that time).
547 pub bus_select: extern "C" fn(peripheral_id: crate::FfiOption<u8>),
548 /// Find out some details about each particular Neotron Bus Peripheral.
549 pub bus_get_info: extern "C" fn(peripheral_id: u8) -> crate::FfiOption<bus::PeripheralInfo>,
550 /// Transact with the currently selected Neotron Bus Peripheral.
551 ///
552 /// You should select a peripheral with `bus_select` first,
553 /// however you can send unselected traffic (e.g. to configure an SD Card
554 /// into SPI mode).
555 ///
556 /// * `tx` - the first list of bytes to send (use `&[]` if not required)
557 /// * `tx2` - the second (and optional) list of bytes to send (use `&[]` if not required)
558 /// * `rx` - the buffer to fill with read data (use `&mut []` if not required)
559 ///
560 /// Because SPI is full-duplex, we discard incoming bytes during the TX
561 /// portion. We must also clock out *something* during the RX portion,
562 /// and we chose `0xFF` bytes. If that doesn't work, use `bus_exchange`.
563 ///
564 /// ```no_run
565 /// # let api = neotron_common_bios::Api::make_dummy_api().unwrap();
566 /// # use neotron_common_bios::{FfiByteSlice, FfiBuffer, FfiOption};
567 /// // Grab Peripheral 1 on the bus
568 /// let _ = (api.bus_select)(FfiOption::Some(1));
569 /// // Read 16 bytes from Register 0 of the selected peripheral
570 /// let mut buf = [0u8; 16];
571 /// let _ = (api.bus_write_read)(FfiByteSlice::new(&[0, 16]), FfiByteSlice::empty(), FfiBuffer::new(&mut buf));
572 /// // Write those bytes to Register 2. You can see now why it's useful to
573 /// // have *two* TX buffers in the API
574 /// let _ = (api.bus_write_read)(FfiByteSlice::new(&[2, 16]), FfiByteSlice::new(&buf), FfiBuffer::empty());
575 /// // Release the bus
576 /// let _ = (api.bus_select)(FfiOption::None);
577 /// # Ok::<(), neotron_common_bios::Error>(())
578 /// ```
579 pub bus_write_read:
580 extern "C" fn(tx: FfiByteSlice, tx2: FfiByteSlice, rx: FfiBuffer) -> crate::ApiResult<()>,
581 /// Exchange bytes with the currently selected Neotron Bus Peripheral.
582 ///
583 /// You should select a peripheral with `bus_select` first,
584 /// however you can send unselected traffic (e.g. to configure an SD Card
585 /// into SPI mode).
586 ///
587 /// SPI is full-duplex, and this routine clocks out the bytes in `buffer`
588 /// one at a time, and replaces them with the bytes received from the
589 /// peripheral.
590 ///
591 /// ```no_run
592 /// # let api = neotron_common_bios::Api::make_dummy_api().unwrap();
593 /// # use neotron_common_bios::{FfiByteSlice, FfiBuffer, FfiOption};
594 /// // Grab Peripheral 1 on the bus
595 /// let _ = (api.bus_select)(FfiOption::Some(1));
596 /// // Exchange four bytes with the peripheral
597 /// let mut buf = [0, 1, 2, 3];
598 /// let _ = (api.bus_exchange)(FfiBuffer::new(&mut buf));
599 /// // buf now contains whatever the peripheral sent us.
600 /// // Release the bus
601 /// let _ = (api.bus_select)(FfiOption::None);
602 /// # Ok::<(), neotron_common_bios::Error>(())
603 /// ```
604 pub bus_exchange: extern "C" fn(buffer: FfiBuffer) -> crate::ApiResult<()>,
605 /// Get bus interrupt status.
606 ///
607 /// Up to 32 interrupts can be returned as a single 32-bit value. A bit is
608 /// set when the interrupt is pending. There is no masking - ignore the bits
609 /// you don't care about.
610 pub bus_interrupt_status: extern "C" fn() -> u32,
611
612 // ========================================================================
613 // Block Device Support
614 // ========================================================================
615 /// Get information about the Block Devices in the system.
616 ///
617 /// Block Devices are also known as *disk drives*. They can be read from
618 /// (and often written to) but only in units called *blocks* or *sectors*.
619 ///
620 /// The BIOS should enumerate removable devices first, followed by fixed
621 /// devices.
622 ///
623 /// The set of devices is not expected to change at run-time - removal of
624 /// media is indicated with a boolean field in the
625 /// `block_dev::DeviceInfo` structure.
626 pub block_dev_get_info: extern "C" fn(device_id: u8) -> crate::FfiOption<block_dev::DeviceInfo>,
627 /// Eject a disk from the drive.
628 ///
629 /// Will return an error if this device is not removable. Does not return an
630 /// error if the drive is already empty.
631 pub block_dev_eject: extern "C" fn(device_id: u8) -> crate::ApiResult<()>,
632 /// Write one or more sectors to a block device.
633 ///
634 /// The function will block until all data is written. The array pointed
635 /// to by `data` must be `num_blocks * block_size` in length, where
636 /// `block_size` is given by `block_dev_get_info`.
637 ///
638 /// There are no requirements on the alignment of `data` but if it is
639 /// aligned, the BIOS may be able to use a higher-performance code path.
640 pub block_write: extern "C" fn(
641 device_id: u8,
642 start_block: block_dev::BlockIdx,
643 num_blocks: u8,
644 data: FfiByteSlice,
645 ) -> crate::ApiResult<()>,
646 /// Read one or more sectors to a block device.
647 ///
648 /// The function will block until all data is read. The array pointed
649 /// to by `data` must be `num_blocks * block_size` in length, where
650 /// `block_size` is given by `block_dev_get_info`.
651 ///
652 /// There are no requirements on the alignment of `data` but if it is
653 /// aligned, the BIOS may be able to use a higher-performance code path.
654 pub block_read: extern "C" fn(
655 device_id: u8,
656 start_block: block_dev::BlockIdx,
657 num_blocks: u8,
658 data: FfiBuffer,
659 ) -> crate::ApiResult<()>,
660 /// Verify one or more sectors on a block device (that is read them and
661 /// check they match the given data).
662 ///
663 /// The function will block until all data is verified. The array pointed
664 /// to by `data` must be `num_blocks * block_size` in length, where
665 /// `block_size` is given by `block_dev_get_info`.
666 ///
667 /// There are no requirements on the alignment of `data` but if it is
668 /// aligned, the BIOS may be able to use a higher-performance code path.
669 pub block_verify: extern "C" fn(
670 device_id: u8,
671 start_block: block_dev::BlockIdx,
672 num_blocks: u8,
673 data: FfiByteSlice,
674 ) -> crate::ApiResult<()>,
675
676 // ========================================================================
677 // Power management functions
678 // ========================================================================
679 /// The OS will call this function when it's idle.
680 ///
681 /// On a microcontroller, this will wait for interrupts. Running in an
682 /// emulator, this will sleep the thread for a while.
683 pub power_idle: extern "C" fn(),
684 /// The OS will call this function to control power on this system.
685 ///
686 /// This function will not return, because the system will be switched off
687 /// before it can return. In the event on an error, this function will hang
688 /// instead.
689 pub power_control: extern "C" fn(mode: FfiPowerMode) -> !,
690
691 // ========================================================================
692 // Mutex functions
693 // ========================================================================
694 /// Performs a compare-and-swap on `value`.
695 ///
696 /// * If `value == old_value`, sets `value = new_value` and returns `true`
697 /// * If `value != old_value`, returns `false`
698 pub compare_and_swap_bool: extern "C" fn(
699 value: &core::sync::atomic::AtomicBool,
700 old_value: bool,
701 new_value: bool,
702 ) -> bool,
703}
704
705// ============================================================================
706// Impls
707// ============================================================================
708
709impl Api {
710 /// This function only exists to make the doctests compile.
711 ///
712 /// It always returns `None`.
713 #[doc(hidden)]
714 pub fn make_dummy_api() -> core::option::Option<Api> {
715 None
716 }
717}
718
719// ============================================================================
720// End of File
721// ============================================================================