opentitanlib/backend/
mod.rs1use anyhow::Result;
6use clap::{Args, FromArgMatches};
7use std::path::{Path, PathBuf};
8use thiserror::Error;
9
10use crate::app::config::process_config_file;
11use crate::app::{TransportWrapper, TransportWrapperBuilder};
12use crate::transport::{EmptyTransport, Transport};
13use crate::util::parse_int::ParseInt;
14
15pub trait Backend {
16 type Opts: Args;
18
19 fn create_transport(common: &BackendOpts, opts: &Self::Opts) -> Result<Box<dyn Transport>>;
21}
22
23#[doc(hidden)]
24pub use inventory::submit;
25
26#[doc(hidden)]
27pub struct InterfaceRegistration {
28 pub name: &'static str,
29 pub augment_args: fn(clap::Command) -> clap::Command,
30 pub create_transport: fn(&BackendOpts, &clap::ArgMatches) -> Result<Box<dyn Transport>>,
31 pub config: Option<&'static str>,
32}
33
34impl InterfaceRegistration {
35 pub const fn of<T: Backend>(name: &'static str, config: Option<&'static str>) -> Self {
36 Self {
37 name,
38 augment_args: |command| {
39 let id = T::Opts::group_id();
40
41 if command
43 .get_groups()
44 .any(|x| Some(x.get_id()) == id.as_ref())
45 {
46 return command;
47 }
48
49 T::Opts::augment_args(command)
50 },
51 create_transport: |common, arg_matches| {
52 let opts = T::Opts::from_arg_matches(arg_matches)?;
53 T::create_transport(common, &opts)
54 },
55 config,
56 }
57 }
58}
59
60inventory::collect!(InterfaceRegistration);
61
62#[macro_export]
63macro_rules! define_interface {
64 ($name: literal, $backend: ty) => {
65 $crate::backend::define_interface!($name, $backend, None);
66 };
67 ($name: literal, $backend: ty, $config: literal) => {
68 $crate::backend::define_interface!($name, $backend, Some($config));
69 };
70 ($name: literal, $backend: ty, $config: expr) => {
71 $crate::backend::submit!($crate::backend::InterfaceRegistration::of::<$backend>(
72 $name, $config
73 ));
74 };
75}
76pub use crate::define_interface;
77
78struct TransportSpecificOpts {
79 matches: clap::ArgMatches,
80}
81
82impl std::fmt::Debug for TransportSpecificOpts {
83 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 fmt.debug_struct("TransportSpecificOpts").finish()
85 }
86}
87
88impl clap::FromArgMatches for TransportSpecificOpts {
89 fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
90 Ok(Self {
91 matches: matches.clone(),
92 })
93 }
94
95 fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
96 unreachable!()
97 }
98}
99
100impl Args for TransportSpecificOpts {
101 fn augment_args(mut cmd: clap::Command) -> clap::Command {
102 for reg in inventory::iter::<InterfaceRegistration> {
103 cmd = (reg.augment_args)(cmd);
104 }
105 cmd
106 }
107
108 fn augment_args_for_update(_: clap::Command) -> clap::Command {
109 unreachable!()
110 }
111}
112
113#[derive(Debug, Args)]
114pub struct BackendOpts {
115 #[arg(long, default_value = "")]
117 pub interface: String,
118
119 #[arg(long)]
124 pub disable_dft_on_reset: bool,
125
126 #[arg(long, value_parser = u16::from_str)]
128 pub usb_vid: Option<u16>,
129 #[arg(long, value_parser = u16::from_str)]
131 pub usb_pid: Option<u16>,
132 #[arg(long)]
134 pub usb_serial: Option<String>,
135
136 #[command(flatten)]
137 transport_specific_opts: TransportSpecificOpts,
138
139 #[arg(long, num_args = 1)]
141 pub conf: Vec<PathBuf>,
142
143 #[arg(long)]
148 pub openocd_adapter_config: Option<PathBuf>,
149}
150
151#[derive(Error, Debug)]
152pub enum Error {
153 #[error("Unknown interface {0}")]
154 UnknownInterface(String),
155}
156
157pub fn create(args: &BackendOpts) -> Result<TransportWrapper> {
159 let interface = args.interface.as_str();
160 let mut env = TransportWrapperBuilder::new(interface.to_string(), args.disable_dft_on_reset);
161
162 for conf_file in &args.conf {
163 process_config_file(&mut env, conf_file.as_ref())?
164 }
165
166 let interface = env.get_interface();
167
168 let (backend, default_conf) = 'done: {
169 for reg in inventory::iter::<InterfaceRegistration> {
170 if interface == reg.name {
171 let transport =
172 (reg.create_transport)(args, &args.transport_specific_opts.matches)?;
173 break 'done (transport, reg.config.map(Path::new));
174 }
175 }
176
177 Err(Error::UnknownInterface(interface.to_string()))?
178 };
179
180 if args.conf.is_empty()
181 && let Some(conf_file) = default_conf
182 {
183 process_config_file(&mut env, conf_file)?
184 }
185 env.set_openocd_adapter_config(&args.openocd_adapter_config);
186 env.build(backend)
187}
188
189pub fn create_empty_transport() -> Result<Box<dyn Transport>> {
190 Ok(Box::new(EmptyTransport))
191}
192
193struct EmptyBackend;
194
195impl Backend for EmptyBackend {
196 type Opts = ();
197
198 fn create_transport(_: &BackendOpts, _: &()) -> Result<Box<dyn Transport>> {
199 Ok(Box::new(EmptyTransport))
200 }
201}
202
203define_interface!("", EmptyBackend);