@@ -2,8 +2,63 @@ use crate::{return_variant, ErrorCode, ReturnVariant};
2
2
3
3
use core:: mem:: transmute;
4
4
5
- /// The response type from `command`. Can represent a successful value or a
6
- /// failure.
5
+ /// The response type from the [`command`](crate::Syscalls::command) syscall.
6
+ /// Can represent a success or a failure with or without associated data.
7
+ ///
8
+ /// After a syscall is made, registers `r1`-`r3` contain the output as
9
+ /// described by [TRD 104][trd-104]. Some syscalls only return success/failure,
10
+ /// while others provide associated data. This is done by placing the _return
11
+ /// variant_ in `r0`, which specifies how the output registers should be
12
+ /// interpreted. For syscalls other than `command`, the possible output
13
+ /// variants are fixed; you always know which variants are expected given the
14
+ /// syscall class.
15
+ ///
16
+ /// However, the `command` syscall is flexible - there must be one success
17
+ /// variant and one failure variant for a given driver/command ID, but
18
+ /// which variants those are, and what data is expected, cannot be checked
19
+ /// statically. Capsules and userspace APIs must agree on the expected
20
+ /// variants for success and failure.
21
+ ///
22
+ /// # Example
23
+ ///
24
+ /// This uses the [`to_result`] method to implicitly check variants and convert
25
+ /// to a `Result`.
26
+ ///
27
+ /// ```ignore
28
+ /// let res: Result<(u32, u32), ErrorCode> = Syscalls::command(314, 1, 1, 2).to_result();
29
+ /// match res {
30
+ /// Ok((val1, val2)) => {
31
+ /// // Success with associated data in val1, val2.
32
+ /// }
33
+ /// Err(ErrorCode::BadRVal) => {
34
+ /// // Incorrect return variant! We may choose to handle this
35
+ /// // explicitly or propagate upwards without branching.
36
+ /// }
37
+ /// Err(ec) => {
38
+ /// // The driver returned an error (or it doesn't exist).
39
+ /// }
40
+ /// }
41
+ /// ```
42
+ ///
43
+ /// This uses the `get_*` methods to check the variants explicitly and extract
44
+ /// the associated data.
45
+ ///
46
+ /// ```ignore
47
+ /// let command_return = Syscalls::command(314, 1, 1, 2);
48
+ /// if let Some((val1, val2)) = command_return.get_success_2_u32() {
49
+ /// // If there was a success, there is an associated data (u32, u32).
50
+ /// } else if let Some(error_code) = command_return.get_failure() {
51
+ /// // If there was a failure, there's no associated data and we only
52
+ /// // have an error code.
53
+ /// } else {
54
+ /// // Incorrect return variant! If this occurs, your capsule and userspace
55
+ /// // API do not agree on what the return variants should be.
56
+ /// // An application may want to panic in this case to catch this early.
57
+ /// }
58
+ /// ```
59
+ ///
60
+ /// [trd-104]: https://github.com/tock/tock/blob/master/doc/reference/trd104-syscalls.md#32-return-values
61
+ #[ must_use = "this `CommandReturn` may represent an error, which should be handled" ]
7
62
#[ derive( Clone , Copy , Debug ) ]
8
63
pub struct CommandReturn {
9
64
return_variant : ReturnVariant ,
@@ -26,16 +81,6 @@ impl CommandReturn {
26
81
r3,
27
82
}
28
83
}
29
- // I generally expect CommandReturn to be used with pattern matching, e.g.:
30
- //
31
- // let command_return = Syscalls::command(314, 1, 1, 2);
32
- // if let Some((val1, val2)) = command_return.get_success_2_u32() {
33
- // // ...
34
- // } else if let Some(error_code) = command_return.get_failure() {
35
- // // ...
36
- // } else {
37
- // // Incorrect return variant
38
- // }
39
84
40
85
/// Returns true if this CommandReturn is of type Failure. Note that this
41
86
/// does not return true for other failure types, such as Failure with u32.
@@ -177,4 +222,152 @@ impl CommandReturn {
177
222
pub fn return_variant ( & self ) -> ReturnVariant {
178
223
self . return_variant
179
224
}
225
+
226
+ /// Interprets this `CommandReturn` as a `Result`, checking the success and
227
+ /// failure variants, as well as extracting the relevant data.
228
+ ///
229
+ /// If neither the success or failure variants match what is required by
230
+ /// `T` and `E`, this function will return `Err(ErrorCode::BadRVal)`.
231
+ /// If `E` contains non-`ErrorCode` data in this case, the data will be 0.
232
+ ///
233
+ /// It is recommended to use type ascription or `::<>` to make the types
234
+ /// for `T` and `E` explicit at call-site.
235
+ pub fn to_result < T , E > ( self ) -> Result < T , E >
236
+ where
237
+ T : SuccessData ,
238
+ E : FailureData ,
239
+ {
240
+ let ( return_variant, r1, mut r2, mut r3) = self . raw_values ( ) ;
241
+ if return_variant == T :: RETURN_VARIANT {
242
+ return Ok ( T :: from_raw_values ( r1, r2, r3) ) ;
243
+ }
244
+ let ec: ErrorCode = if return_variant == E :: RETURN_VARIANT {
245
+ // Safety: E::RETURN_VARIANT must be a failure variant, and
246
+ // failure variants must contain a valid ErrorCode in r1.
247
+ unsafe { core:: mem:: transmute ( r1 as u16 ) }
248
+ } else {
249
+ r2 = 0 ;
250
+ r3 = 0 ;
251
+ ErrorCode :: BadRVal
252
+ } ;
253
+ Err ( E :: from_raw_values ( ec, r2, r3) )
254
+ }
255
+ }
256
+
257
+ mod sealed {
258
+ pub trait Sealed { }
259
+ }
260
+
261
+ /// Output from a successful `command` syscall.
262
+ ///
263
+ /// This trait is [sealed], meaning foreign implementations cannot be defined,
264
+ /// even though it can be referenced by foreign crates.
265
+ ///
266
+ /// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
267
+ pub trait SuccessData : sealed:: Sealed {
268
+ /// The return variant for this success type, stored in `r0` after
269
+ /// performing a `command` syscall.
270
+ const RETURN_VARIANT : ReturnVariant ;
271
+
272
+ /// Constructs the success data given the raw register values.
273
+ fn from_raw_values ( r1 : u32 , r2 : u32 , r3 : u32 ) -> Self ;
274
+ }
275
+
276
+ impl sealed:: Sealed for ( ) { }
277
+ impl SuccessData for ( ) {
278
+ const RETURN_VARIANT : ReturnVariant = return_variant:: SUCCESS ;
279
+
280
+ fn from_raw_values ( _r1 : u32 , _r2 : u32 , _r3 : u32 ) -> Self { }
281
+ }
282
+ impl sealed:: Sealed for u32 { }
283
+ impl SuccessData for u32 {
284
+ const RETURN_VARIANT : ReturnVariant = return_variant:: SUCCESS_U32 ;
285
+
286
+ fn from_raw_values ( r1 : u32 , _r2 : u32 , _r3 : u32 ) -> Self {
287
+ r1
288
+ }
289
+ }
290
+ impl sealed:: Sealed for u64 { }
291
+ impl SuccessData for u64 {
292
+ const RETURN_VARIANT : ReturnVariant = return_variant:: SUCCESS_U64 ;
293
+
294
+ fn from_raw_values ( r1 : u32 , r2 : u32 , _r3 : u32 ) -> Self {
295
+ r1 as u64 | ( ( r2 as u64 ) << 32 )
296
+ }
297
+ }
298
+ impl sealed:: Sealed for ( u32 , u32 ) { }
299
+ impl SuccessData for ( u32 , u32 ) {
300
+ const RETURN_VARIANT : ReturnVariant = return_variant:: SUCCESS_2_U32 ;
301
+
302
+ fn from_raw_values ( r1 : u32 , r2 : u32 , _r3 : u32 ) -> Self {
303
+ ( r1, r2)
304
+ }
305
+ }
306
+ impl sealed:: Sealed for ( u32 , u64 ) { }
307
+ impl SuccessData for ( u32 , u64 ) {
308
+ const RETURN_VARIANT : ReturnVariant = return_variant:: SUCCESS_U32_U64 ;
309
+
310
+ fn from_raw_values ( r1 : u32 , r2 : u32 , r3 : u32 ) -> Self {
311
+ ( r1, r2 as u64 | ( ( r3 as u64 ) << 32 ) )
312
+ }
313
+ }
314
+ impl sealed:: Sealed for ( u32 , u32 , u32 ) { }
315
+ impl SuccessData for ( u32 , u32 , u32 ) {
316
+ const RETURN_VARIANT : ReturnVariant = return_variant:: SUCCESS_3_U32 ;
317
+
318
+ fn from_raw_values ( r1 : u32 , r2 : u32 , r3 : u32 ) -> Self {
319
+ ( r1, r2, r3)
320
+ }
321
+ }
322
+
323
+ /// Output from a failed `command` syscall.
324
+ ///
325
+ /// This trait is [sealed], meaning foreign implementations cannot be defined,
326
+ /// even though it can be referenced by foreign crates.
327
+ ///
328
+ /// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
329
+ pub unsafe trait FailureData : sealed:: Sealed {
330
+ /// The return variant for this failure type, stored in `r0` after
331
+ /// performing a `command` syscall.
332
+ ///
333
+ /// # Safety
334
+ /// This must represent a failure variant, such that `r1` will always be
335
+ /// a valid [`ErrorCode`].
336
+ const RETURN_VARIANT : ReturnVariant ;
337
+
338
+ /// Constructs the error data given the raw register values.
339
+ fn from_raw_values ( r1 : ErrorCode , r2 : u32 , r3 : u32 ) -> Self ;
340
+ }
341
+
342
+ impl sealed:: Sealed for ErrorCode { }
343
+ unsafe impl FailureData for ErrorCode {
344
+ const RETURN_VARIANT : ReturnVariant = return_variant:: FAILURE ;
345
+
346
+ fn from_raw_values ( r1 : ErrorCode , _r2 : u32 , _r3 : u32 ) -> Self {
347
+ r1
348
+ }
349
+ }
350
+ impl sealed:: Sealed for ( ErrorCode , u32 ) { }
351
+ unsafe impl FailureData for ( ErrorCode , u32 ) {
352
+ const RETURN_VARIANT : ReturnVariant = return_variant:: FAILURE_U32 ;
353
+
354
+ fn from_raw_values ( r1 : ErrorCode , r2 : u32 , _r3 : u32 ) -> Self {
355
+ ( r1, r2)
356
+ }
357
+ }
358
+ impl sealed:: Sealed for ( ErrorCode , u32 , u32 ) { }
359
+ unsafe impl FailureData for ( ErrorCode , u32 , u32 ) {
360
+ const RETURN_VARIANT : ReturnVariant = return_variant:: FAILURE_2_U32 ;
361
+
362
+ fn from_raw_values ( r1 : ErrorCode , r2 : u32 , r3 : u32 ) -> Self {
363
+ ( r1, r2, r3)
364
+ }
365
+ }
366
+ impl sealed:: Sealed for ( ErrorCode , u64 ) { }
367
+ unsafe impl FailureData for ( ErrorCode , u64 ) {
368
+ const RETURN_VARIANT : ReturnVariant = return_variant:: FAILURE_U64 ;
369
+
370
+ fn from_raw_values ( r1 : ErrorCode , r2 : u32 , r3 : u32 ) -> Self {
371
+ ( r1, r2 as u64 | ( ( r3 as u64 ) << 32 ) )
372
+ }
180
373
}
0 commit comments