Skip to main content

fpgad_cli/
lib.rs

1// This file is part of fpgad, an application to manage FPGA subsystem together with device-tree and kernel modules.
2//
3// Copyright 2025 Canonical Ltd.
4//
5// SPDX-License-Identifier: GPL-3.0-only
6//
7// fpgad is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published by the Free Software Foundation.
8//
9// fpgad is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
10//
11// You should have received a copy of the GNU General Public License along with this program.  If not, see http://www.gnu.org/licenses/.
12
13//! FPGA CLI (fpgad_cli) - Command-line interface for managing FPGA devices.
14//!
15//! This is FPGAd's commandline interface (CLI) . Due to strict confinement of the snap, this can
16//! only be used from a terminal or from a script which is not part of another snap.
17//! It is a useful helper for one-off control of the FPGA device or testing, and serves as an
18//! example implementation for the DBus interface.
19//!
20//! # Common Concepts
21//!
22//! The following concepts are shared across all CLI submodules ([`load`], [`remove`], [`set`], [`status`]).
23//!
24//! ## Device Handles
25//! [Device Handles]: #device-handles
26//!
27//! A "device handle" refers to the name of an FPGA device as it appears in
28//! `/sys/class/fpga_manager/`. Common examples include:
29//! - `fpga0` - The first FPGA device
30//! - `fpga1` - The second FPGA device (if multiple FPGAs are present)
31//!
32//! These handles uniquely identify FPGA devices in the system and are used throughout
33//! the CLI to specify which device to operate on.
34//!
35//! ## Overlay Handles
36//! [Overlay Handles]: #overlay-handles
37//!
38//! An "overlay handle" refers to the name of a device tree overlay as it appears in
39//! `/sys/kernel/config/device-tree/overlays/`. Common examples include:
40//! - `overlay0` - A generic overlay name
41//! - `fpga-design` - A custom overlay name specified during loading
42//!
43//! These handles are used to identify and manage loaded device tree overlays. When loading
44//! an overlay, you can specify a custom handle or let the system choose one based on the
45//! device handle.
46//!
47//! ## Error Handling
48//! [Error Handling]: #error-handling
49//!
50//! All CLI functions communicate with the fpgad daemon via DBus and return
51//! `Result<String, zbus::Error>` (or variants with `Vec<String>` or `HashMap<String, String>`).
52//!
53//! When the fpgad daemon returns an application-level error (not a DBus communication
54//! error), the error will be of type `zbus::Error::Failure` and the error message will
55//! begin with `FpgadError::<variant>:` followed by the error details. For example:
56//! ```text
57//! FpgadError::Argument: Device fpga0 not found.
58//! FpgadError::IOWrite: Failed to write bitstream: Permission denied
59//! FpgadError::IORead: Failed to read state: No such file or directory
60//! ```
61//!
62//! This allows callers to distinguish between:
63//! - **DBus communication errors** - Problems connecting to or communicating with the daemon
64//! - **Application errors** - Errors from the daemon itself (prefixed with `FpgadError::`)
65//!
66//! # Usage
67//!
68//! ```text
69//! Usage: [snap run] fpgad [OPTIONS] <COMMAND>
70//!
71//! OPTIONS:
72//!   -h, --help                      Print help
73//!   -p, --platform <PLATFORM>       Platform override string (bypasses platform detection logic).
74//!                                   When provided, this platform string is passed directly to the
75//!                                   daemon instead of auto-detecting from the device handle.
76//!                                   Examples: "universal", "xlnx,zynqmp-pcap-fpga"
77//!   -d, --device <DEVICE_HANDLE>    FPGA device handle to be used for the operations.
78//!                                   Default value is calculated at runtime - the application
79//!                                   picks the first available FPGA device in the system
80//!                                   (under `/sys/class/fpga_manager/`).
81//!                                   Examples: "fpga0", "fpga1"
82//!
83//! SUBCOMMAND OPTIONS:
84//!   -n, --name <OVERLAY_NAME>       (Used with load/remove overlay subcommands)
85//!                                   Name for the overlay directory in configfs
86//!                                   (under `/sys/kernel/config/device-tree/overlays/`).
87//!                                   If not provided, defaults to the device handle or "overlay0".
88//!
89//! COMMANDS:
90//! ├── load                Load a bitstream or overlay
91//! │   ├── overlay <FILE> [--name <OVERLAY_HANDLE> --platform <PLATFORM>]
92//! │   │       Load overlay (.dtbo) into the system using the default OVERLAY_HANDLE
93//! │   │           (either the provided DEVICE_HANDLE or "overlay0") or provide
94//! │   │       --name: to name the overlay directory
95//! │   └── bitstream <FILE> [--platform <PLATFORM>]
96//! │           Load bitstream (e.g. `.bit.bin` file) into the FPGA
97//! │
98//! ├── set <ATTRIBUTE> <VALUE>
99//! │       Set an attribute/flag under `/sys/class/fpga_manager/<DEVICE_HANDLE>/<ATTRIBUTE>`
100//! │
101//! ├── status [--device <DEVICE_HANDLE> --platform <PLATFORM>]
102//! │       Show FPGA status (all devices and overlays) or provide
103//! │       --device: for a specific device status
104//! │
105//! └── remove              Remove an overlay or bitstream
106//!     ├── overlay [--name <OVERLAY_HANDLE> --platform <PLATFORM>]
107//!     │       Removes the first overlay found (call repeatedly to remove all) or provide
108//!     │       --name: to remove overlay previously loaded with given OVERLAY_HANDLE
109//!     └── bitstream [--name <BITSTREAM_HANDLE> --platform <PLATFORM>]
110//!             Remove active bitstream from FPGA (bitstream removal is vendor specific)
111//! ```
112//!
113//! ### Loading
114//!
115//! ```shell
116//! fpgad [--device=<device_handle>] [--platform=<platform>] load ( (overlay <file> [--name=<overlay_name>]) | (bitstream <file>) )
117//! ```
118//!
119//! ### Removing
120//!
121//! ```shell
122//! fpgad [--device=<device_handle>] [--platform=<platform>] remove ( ( overlay [--name=<overlay_name>] ) | ( bitstream ) )
123//! ```
124//!
125//! ### Set
126//!
127//! ```shell
128//! fpgad [--device=<device_handle>] set ATTRIBUTE VALUE
129//! ```
130//!
131//! ### Status
132//!
133//! ```shell
134//! fpgad [--device=<device_handle>] [--platform=<platform>] status
135//! ```
136//!
137//! ## examples (for testing)
138//!
139//! ### Load
140//!
141//! ```shell
142//! sudo ./target/debug/cli load bitstream /lib/firmware/k26-starter-kits.bit.bin
143//! sudo ./target/debug/cli --device=fpga0 load bitstream /lib/firmware/k26-starter-kits.bit.bin
144//! sudo ./target/debug/cli --platform=universal load bitstream /lib/firmware/k26-starter-kits.bit.bin
145//! sudo ./target/debug/cli --platform=xlnx load bitstream /lib/firmware/k26-starter-kits.bit.bin
146//!
147//! sudo ./target/debug/cli load overlay /lib/firmware/k26-starter-kits.dtbo
148//! sudo ./target/debug/cli load overlay /lib/firmware/k26-starter-kits.dtbo --name=overlay_handle
149//! sudo ./target/debug/cli --device=fpga0 load overlay /lib/firmware/k26-starter-kits.dtbo --name=overlay_handle
150//! sudo ./target/debug/cli --platform=universal load overlay /lib/firmware/k26-starter-kits.dtbo --name=overlay_handle
151//! sudo ./target/debug/cli --platform=xlnx --device=fpga0 load overlay /lib/firmware/k26-starter-kits.dtbo --name=overlay_handle
152//! ```
153//!
154//! ### Remove
155//!
156//! ```shell
157//! sudo ./target/debug/cli --device=fpga0 remove overlay
158//! sudo ./target/debug/cli --device=fpga0 remove overlay --name=overlay_handle
159//! ```
160//!
161//! ### Set
162//!
163//! ```shell
164//! sudo ./target/debug/cli set flags 0
165//! sudo ./target/debug/cli --device=fpga0 set flags 0
166//! ```
167//!
168//! ### Status
169//!
170//! ```shell
171//! ./target/debug/cli status
172//! ./target/debug/cli --device=fpga0 status
173//! ```
174
175mod proxies;
176
177pub mod load;
178
179pub mod remove;
180
181pub mod status;
182
183pub mod set;
184
185use clap::{Parser, Subcommand};
186
187/// Command-line interface structure for FPGA management operations.
188///
189/// This structure represents the top-level CLI interface for interacting with FPGA devices
190/// through the fpgad daemon's DBus interface. It provides a unified interface for loading
191/// bitstreams and overlays, querying device status, setting attributes, and removing
192/// loaded components.
193///
194/// # Examples
195///
196/// ```shell
197///
198/// # Load a bitstream
199/// fpgad load bitstream /lib/firmware/design.bit.bin
200///
201/// # Check status of all FPGA devices
202/// fpgad status
203///
204/// # Load an overlay with a specific name
205/// fpgad load overlay /lib/firmware/overlay.dtbo --name=my_overlay
206///
207/// ```
208#[derive(Parser, Debug)]
209#[command(name = "fpga")]
210#[command(bin_name = "fpga")]
211pub struct Cli {
212    /// Platform override string (bypasses platform detection logic).
213    /// When provided, this platform string is passed directly to the daemon
214    /// instead of auto-detecting from the device handle.
215    /// Examples: "universal", "xlnx,zynqmp-pcap-fpga"
216    #[arg(short = 'p', long = "platform")]
217    platform: Option<String>,
218
219    /// FPGA `device` handle to be used for the operations.
220    /// Default value is calculated at runtime - the application picks the first
221    /// available FPGA device in the system (under /sys/class/fpga_manager/).
222    /// Examples: "fpga0", "fpga1"
223    #[arg(short = 'd', long = "device")]
224    device: Option<String>,
225
226    #[command(subcommand)]
227    command: Commands,
228}
229
230impl Cli {
231    /// Returns the platform override string, if provided.
232    pub fn platform(&self) -> Option<&String> {
233        self.platform.as_ref()
234    }
235
236    /// Returns the device handle, if provided.
237    pub fn device(&self) -> Option<&String> {
238        self.device.as_ref()
239    }
240
241    /// Returns a reference to the command.
242    pub fn command(&self) -> &Commands {
243        &self.command
244    }
245}
246
247/// Subcommands for loading FPGA components.
248///
249/// This enum defines the types of components that can be loaded onto an FPGA device:
250/// - **Overlay**: Device tree overlays (.dtbo files) that describe hardware configuration
251/// - **Bitstream**: FPGA configuration bitstreams (.bit.bin files) containing the actual FPGA design
252///
253/// Device tree overlays are typically loaded before or after bitstreams to properly configure
254/// the kernel's view of the FPGA's hardware interfaces and peripherals.
255///
256/// # Examples
257///
258/// ```shell
259/// # Load a bitstream
260/// fpgad load bitstream [-d=<DEVICE_HANDLE> -p=<COMPAT_STR>] /lib/firmware/design.bit.bin
261///
262/// # Load an overlay with a custom name
263/// fpgad load overlay [-d=<DEVICE_HANDLE> -p=<COMPAT_STR>] /lib/firmware/overlay.dtbo [-n=my_overlay]
264/// ```
265#[derive(Subcommand, Debug)]
266pub enum LoadSubcommand {
267    /// Load overlay into the system
268    Overlay {
269        /// Overlay `FILE` to be loaded (typically .dtbo)
270        file: String,
271
272        /// Name for the overlay directory which will be created
273        /// under "/sys/kernel/config/device-tree/overlays/".
274        /// If not provided, defaults to the device handle or "overlay0".
275        #[arg(short = 'n', long = "name")]
276        name: Option<String>,
277    },
278    /// Load bitstream into the system
279    Bitstream {
280        /// Bitstream `FILE` to be loaded (typically .bit.bin)
281        file: String,
282    },
283}
284
285/// Subcommands for removing FPGA components.
286///
287/// This enum defines the types of components that can be removed from an FPGA device:
288/// - **Overlay**: Removes a device tree overlay by its name.
289/// - **Bitstream**: Intended to remove the currently loaded FPGA bitstream (vendor-specific
290///   operation that may use slot identifiers on platforms like DFX Manager)
291///
292/// Removing overlays is important for proper cleanup when reconfiguring the FPGA.
293/// Bitstream removal support depends on the FPGA vendor and platform capabilities.
294///
295/// # Examples
296///
297/// ```shell
298/// # Remove the first overlay found
299/// fpgad remove overlay
300///
301/// # Remove a specific overlay by name
302/// fpgad [-d=<DEVICE_HANDLE>] [-p=<COMPAT_STR>] remove overlay -n=my_overlay
303///
304/// # Remove a bitstream, if supported
305/// fpgad [-d=<DEVICE_HANDLE>] [-p=<COMPAT_STR>] remove bitstream -n=0 # for dfx-mgr slot 0
306/// ```
307#[derive(Subcommand, Debug)]
308pub enum RemoveSubcommand {
309    /// Remove overlay with the name provided
310    Overlay {
311        /// Name of the overlay to remove (as given during `load` operation).
312        /// If not provided, removes the first overlay found in the system.
313        /// This is different from device_handle which is used for platform detection.
314        #[arg(short = 'n', long = "name")]
315        name: Option<String>,
316    },
317    /// Remove bitstream loaded in the given device
318    Bitstream {
319        /// Handle/identifier for the bitstream to remove.
320        /// For DFX Manager platforms, this can be a slot ID.
321        /// Use empty string "" to remove the latest bitstream.
322        #[arg(long = "handle")]
323        handle: Option<String>,
324    },
325}
326
327/// Top-level commands supported by the CLI.
328///
329/// This enum represents all the primary operations available through the fpgad CLI:
330/// - **Load**: Load bitstreams or device tree overlays onto the FPGA
331/// - **Set**: Configure FPGA attributes and flags (e.g., programming flags)
332/// - **Status**: Query the current state of FPGA devices and loaded overlays
333/// - **Remove**: Unload bitstreams or device tree overlays from the FPGA
334///
335/// Each command communicates with the fpgad daemon via DBus to perform privileged
336/// operations on FPGA devices managed through the Linux kernel's FPGA subsystem.
337///
338/// # Examples
339///
340/// ```shell
341/// # Load a bitstream to a specific device
342/// fpgad --device=fpga0 load bitstream /lib/firmware/design.bit.bin
343///
344/// # Load an overlay with platform override
345/// fpgad --platform=universal load overlay /lib/firmware/overlay.dtbo --name=my_overlay
346///
347/// # Set flags for a device
348/// fpgad --device=fpga0 set flags 0
349///
350/// # Get status for all devices
351/// fpgad status
352///
353/// # Remove an overlay by name
354/// fpgad remove overlay --name=my_overlay
355/// ```
356#[derive(Subcommand, Debug)]
357pub enum Commands {
358    /// Load a bitstream or an overlay for the given device handle
359    Load {
360        #[command(subcommand)]
361        command: LoadSubcommand,
362    },
363    /// Set an option (e.g. flags) to a specific value for a given device handle
364    Set { attribute: String, value: String },
365    /// Get the status information for the given device handle
366    Status,
367    /// Remove bitstream or an overlay
368    Remove {
369        #[command(subcommand)]
370        command: RemoveSubcommand,
371    },
372}