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// ============================================================================