mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2025-01-24 14:58:09 +00:00
Merge branch 'master' of github.com:ferrous-systems/embedded-trainings-2020 into edit-book
This commit is contained in:
commit
1350b44e79
11 changed files with 118 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
|||
//! Some USB 2.0 data types
|
||||
// NOTE this is a solution to exercise `usb-3`
|
||||
// NOTE this is a partial solution to exercise `usb-4`
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
|
@ -1,5 +1,5 @@
|
|||
//! Some USB 2.0 data types
|
||||
// NOTE this is a solution to exercise `usb-2`
|
||||
// NOTE this is a partial solution to exercise `usb-2`
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
|
@ -1,5 +1,5 @@
|
|||
//! Some USB 2.0 data types
|
||||
// NOTE this is a solution to exercise `usb-2`
|
||||
// NOTE this is a partial solution to exercise `usb-4`
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
42
advanced/linux-configured.txt
Normal file
42
advanced/linux-configured.txt
Normal file
|
@ -0,0 +1,42 @@
|
|||
INFO:usb_5 -- USB: UsbReset @ 397.15576ms
|
||||
INFO:usb_5 -- USB reset condition detected
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 470.00122ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: Device, length: 64 }
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_5 -- USB: UsbEp0DataDone @ 470.306395ms
|
||||
INFO:usb_5 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_5 -- USB: UsbReset @ 520.721433ms
|
||||
INFO:usb_5 -- USB reset condition detected
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 593.292235ms
|
||||
INFO:usb_5 -- EP0: SetAddress { address: Some(21) }
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 609.954832ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: Device, length: 18 }
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_5 -- USB: UsbEp0DataDone @ 610.260008ms
|
||||
INFO:usb_5 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 610.443113ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: DeviceQualifier, length: 10 }
|
||||
WARN:usb_5 -- EP0IN: stalled
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 610.809325ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: DeviceQualifier, length: 10 }
|
||||
WARN:usb_5 -- EP0IN: stalled
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 611.175535ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: DeviceQualifier, length: 10 }
|
||||
WARN:usb_5 -- EP0IN: stalled
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 611.511228ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: Configuration { index: 0 }, length: 9 }
|
||||
INFO:dk::usbd -- EP0IN: start 9B transfer
|
||||
INFO:usb_5 -- USB: UsbEp0DataDone @ 611.846922ms
|
||||
INFO:usb_5 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 612.030027ms
|
||||
INFO:usb_5 -- EP0: GetDescriptor { descriptor: Configuration { index: 0 }, length: 18 }
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_5 -- USB: UsbEp0DataDone @ 612.365721ms
|
||||
INFO:usb_5 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_5 -- USB: UsbEp0Setup @ 612.640378ms
|
||||
INFO:usb_5 -- EP0: SetConfiguration { value: Some(42) }
|
||||
INFO:usb_5 -- entering the configured state
|
42
advanced/linux-enumeration.txt
Normal file
42
advanced/linux-enumeration.txt
Normal file
|
@ -0,0 +1,42 @@
|
|||
INFO:usb_4 -- USB: UsbReset @ 318.66455ms
|
||||
INFO:usb_4 -- USB reset condition detected
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 391.418456ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: Device, length: 64 }
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_4 -- USB: UsbEp0DataDone @ 391.723632ms
|
||||
INFO:usb_4 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_4 -- USB: UsbReset @ 442.016601ms
|
||||
INFO:usb_4 -- USB reset condition detected
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 514.709471ms
|
||||
INFO:usb_4 -- EP0: SetAddress { address: Some(17) }
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 531.37207ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: Device, length: 18 }
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_4 -- USB: UsbEp0DataDone @ 531.646727ms
|
||||
INFO:usb_4 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 531.829832ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: DeviceQualifier, length: 10 }
|
||||
ERROR:usb_4 -- EP0IN: unexpected request; stalling the endpoint
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 532.226562ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: DeviceQualifier, length: 10 }
|
||||
ERROR:usb_4 -- EP0IN: unexpected request; stalling the endpoint
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 532.592772ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: DeviceQualifier, length: 10 }
|
||||
ERROR:usb_4 -- EP0IN: unexpected request; stalling the endpoint
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 533.020018ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: Configuration { index: 0 }, length: 9 }
|
||||
INFO:dk::usbd -- EP0IN: start 9B transfer
|
||||
INFO:usb_4 -- USB: UsbEp0DataDone @ 533.386228ms
|
||||
INFO:usb_4 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 533.569335ms
|
||||
INFO:usb_4 -- EP0: GetDescriptor { descriptor: Configuration { index: 0 }, length: 18 }
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_4 -- USB: UsbEp0DataDone @ 533.935546ms
|
||||
INFO:usb_4 -- EP0IN: transfer complete
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_4 -- USB: UsbEp0Setup @ 534.118651ms
|
||||
INFO:usb_4 -- EP0: SetConfiguration { value: Some(42) }
|
||||
ERROR:usb_4 -- EP0IN: unexpected request; stalling the endpoint
|
|
@ -6,10 +6,7 @@ An instance of this abstraction is available in the `board` value (`#[init]` fun
|
|||
|
||||
The `Ep0In` API has two methods: `start` and `end` (also see their API documentation). `start` is used to start a DATA stage; this method takes a *slice of bytes* (`[u8]`) as argument; this argument is the response data. The `end` method needs to be called after `start`, when the EP0DATADONE event is raised, to complete the control transfer. `Ep0In` will automatically issue the STATUS stage that must follow the DATA stage.
|
||||
|
||||
To implement responding to a GET_DESCRIPTOR request, do the following:
|
||||
|
||||
1. **Extend the GET_DESCRIPTOR parser implementation to handle a CONFIGURATION request:** make the `common/usb/lib.rs::get_descriptor_configuration()` test run successfully.
|
||||
2. **Answer the Descriptor Request:** extend `usb-3.rs` so that it uses `Ep0In` to respond to the `GET_DESCRIPTOR Device` request (and only to that request). The response must be a device descriptor with its fields set to these values:
|
||||
To implement responding to a GET_DESCRIPTOR Device request, extend `usb-3.rs` so that it uses `Ep0In` to respond to the `GET_DESCRIPTOR Device` request (and only to that request). The response must be a device descriptor with its fields set to these values:
|
||||
|
||||
- `bDeviceClass = bDeviceSubClass = bDeviceProtocol = 0`, these are unimportant for enumeration
|
||||
- `bMaxPacketSize0 = 64`, this is the most performant option (minimizes exchanges between the device and the host) and it's assumed by the `Ep0In` abstraction
|
||||
|
|
|
@ -37,7 +37,7 @@ To handle a SET_CONFIGURATION, do the following:
|
|||
- if `wValue` is non-zero and valid (was previously reported in a configuration descriptor) then move to the `Configured` state with the new configuration value
|
||||
- if `wValue` is not valid then stall the endpoint
|
||||
|
||||
In all the cases where you did not stall the endpoint (by returning `Err`) you'll need to acknowledge the request by starting a STATUS stage.
|
||||
In all the cases where you did not stall the endpoint (by returning `Err`) you'll need to acknowledge the request by starting a STATUS stage.
|
||||
This is done by writing 1 to the TASKS_EP0STATUS register.
|
||||
|
||||
NOTE: On Windows, you may get a `GET_STATUS` request *before* the `SET_CONFIGURATION` request and although you *should* respond to it, stalling the `GET_STATUS` request seems sufficient to get the device to the `Configured` state.
|
||||
|
@ -91,8 +91,9 @@ INFO:usb_5 -- EP0: SetConfiguration { value: Some(42) }
|
|||
INFO:usb_5 -- entering the configured state
|
||||
```
|
||||
|
||||
These logs are from a Linux host. You can find traces for other OSes in these files (they are next to this README):
|
||||
These logs are from a Linux host. You can find traces for other OSes in these files (they are in the `advanced` folder):
|
||||
|
||||
- `linux-configured.txt` (same logs as the ones shown above)
|
||||
- `win-configured.txt`, this file only contains the logs produced by running `print-descs`
|
||||
- `macos-configured.txt`
|
||||
|
||||
|
|
|
@ -49,10 +49,11 @@ ERROR:usb_4 -- EP0IN: unexpected request; stalling the endpoint
|
|||
|
||||
Note that these logs are from a Linux host where a `SET_CONFIGURATION` request is sent after the `SET_ADDRESS` request. On other OSes you may not get that request before the bus goes idle. Also note that there are some `GET_DESCRIPTOR DeviceQualifier` requests in this case; you do not need to parse them in the `usb` crate as they'll be rejected (stalled) anyways.
|
||||
|
||||
You can find traces for other OSes in these files (they are next to this README):
|
||||
You can find traces for other OSes in these files (they are in the `advanced` folder):
|
||||
|
||||
- `win-enumeration.txt`
|
||||
- `linux-enumeration.txt` (same logs as the ones shown above)
|
||||
- `macos-enumeration.txt`
|
||||
- `win-enumeration.txt`
|
||||
|
||||
At this point you can double check that the enumeration works by running the [`usb-list` tool](#listing-usb-devices) while `usb-4.rs` is running.
|
||||
|
||||
|
@ -65,4 +66,3 @@ Bus 001 Device 016: ID 2020:0717 <- nRF52840 on the nRF52840 Development Kit
|
|||
Both the J-Link and the nRF52840 should appear in the list.
|
||||
|
||||
You can find a working solution up to this point in `src/bin/usb-4-solution.rs`. Note that the solution uses the `usb2` crate to parse SETUP packets and that crate supports parsing all standard requests.
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ The Dongle will respond differently depending on the length of the incoming pack
|
|||
|
||||
The Dongle will always respond with packets that are valid UTF-8 so you can use `str::from_utf8` on the response packets.
|
||||
|
||||
Our suggestion is to use a dictionary / map. `std::collections::HashMap` is not available in `no_std` code (without linking to a global allocator) but you can use one of the stack-allocated maps in the [`heapless`] crate. A `Vec`-like buffer may also come in handy; `heapless` provides a stack-allocated, fixed-capacity version of the `std::Vec` type.
|
||||
Our suggestion is to use a dictionary / map. `std::collections::HashMap` is not available in `no_std` code (without linking to a global allocator) but you can use one of the stack-allocated maps in the [`heapless`] crate. It supplies a stack-allocated, fixed-capacity version of the `std::Vec` type which will come in handy to store byte arrays. To store character mappings we recommend using a `heapless::IndexMap`.
|
||||
|
||||
`heapless` is already declared as a dependency in the Cargo.toml of the project so you can directly import it into the application code using a `use` statement.
|
||||
|
||||
|
@ -26,13 +26,26 @@ Our suggestion is to use a dictionary / map. `std::collections::HashMap` is not
|
|||
[crates.io]: https://crates.io/crates/heapless
|
||||
|
||||
|
||||
|
||||
``` rust
|
||||
use heapless::IndexMap; // a dictionary / map
|
||||
use heapless::Vec; // like `std::Vec` but stack-allocated
|
||||
use heapless::Vec; // like `std::Vec` but stack-allocated
|
||||
use heapless::FnvIndexMap; // a dictionary / map
|
||||
use heapless::consts::*; // defines U16, U32, U64... etc. to set the size of the IndexMap
|
||||
|
||||
fn main() {
|
||||
// A hash map with a capacity of 16 key-value pairs allocated on the stack
|
||||
let mut my_map = FnvIndexMap::<_, _, U16>::new();
|
||||
my_map.insert(b'A', b'~').unwrap();
|
||||
|
||||
// A vector with a fixed capacity of 8 elements allocated on the stack
|
||||
let mut my_vec = Vec::<_, U8>::new();
|
||||
my_vec.push(b'A').unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
If you haven't use a stack-allocated collection before note that you'll need to specify the capacity of the collection as a type parameter using one of the "type-level values" in the `heapless::consts` module. The [crate level documentation][`heapless`] of the `heapless` crate has some examples.
|
||||
If you haven't used a stack-allocated collection before note that you'll need to specify the capacity of the collection as a type parameter using one of the "type-level values" in the `heapless::consts` module. The [`heapless::IndexMap` documentation][indexMap] of the `heapless` crate has some usage examples, as does the [`heapless::Vec` documentation][vec].
|
||||
|
||||
[indexMap]: https://docs.rs/heapless/0.5.5/heapless/struct.IndexMap.html
|
||||
[vec]: https://docs.rs/heapless/0.5.5/heapless/struct.Vec.html
|
||||
|
||||
Something you will likely run into while solving this exercise are *character* literals (`'c'`) and *byte* literals (`b'c'`). The former has type [`char`] and represent a single Unicode "scalar value". The latter has type `u8` (1-byte integer) and it's mainly a convenience for getting the value of ASCII characters, for instance `b'A'` is the same as the `65u8` literal.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# USB-2: SETUP Stage
|
||||
|
||||
At the end of program `usb-1` we received a EP0SETUP event. This event signals the *end* of the SETUP stage of a control transfer. The nRF52840 USBD peripheral will automatically receive the SETUP data and store it in the registers BMREQUESTTYPE, BREQUEST, WVALUE{L,H}, WINDEX{L,H} and WLENGTH{L,H}.
|
||||
At the end of program `usb-1` we received a EP0SETUP event. This event signals the *end* of the SETUP stage of a control transfer. The nRF52840 USBD peripheral will automatically receive the SETUP data and store it in the registers BMREQUESTTYPE, BREQUEST, WVALUE{L,H}, WINDEX{L,H} and WLENGTH{L,H}.
|
||||
In `usb-2.rs`, you will find a short description of each register above the variable into which it should be read.
|
||||
|
||||
> For in-depth register documentation, refer to sections 6.35.13.31 to 6.35.13.38 of the [nRF52840 Product Specification][nrf product spec].
|
||||
|
@ -32,7 +32,7 @@ Modify `Request::parse()` in `advanced/common/usb/src/lib.rs` to recognize a GET
|
|||
- remember that you can define binary literals by prefixing them with `0b`
|
||||
- you can use bit shifts (`>>`) and casts (`as u8`) to get the high/low bytes of a `u16`
|
||||
|
||||
See `advanced/common/usb/src/get-descriptor-device.rs` for a solution.
|
||||
See `advanced/common/usb/solution-get-descriptor-device.rs` for a solution.
|
||||
|
||||
2. **Read incoming request information and pass it to the parser:**
|
||||
modify `usb-2.rs` to read `USBD` registers and parse the SETUP data when an EPSETUP event is received.
|
||||
|
@ -54,5 +54,5 @@ INFO:usb_2 -- Goal reached; move to the next section
|
|||
|
||||
`wlength` / `length` can vary depending on the OS, USB port (USB 2.0 vs USB 3.0) or the presence of a USB hub so you may see a different value.
|
||||
|
||||
You can find a solution to step 1. in `advanced/common/usb/get-descriptor-device.rs`.
|
||||
You can find a solution to step 1. in `advanced/common/usb/solution-get-descriptor-device.rs`.
|
||||
You can find a solution to step 2. in `advanced/firmware/src/bin/usb-2-solution.rs`.
|
||||
|
|
|
@ -11,7 +11,7 @@ For each green test, you can extend `usb-4.rs` to handle the new requests your p
|
|||
|
||||
If you need a reference, you can find solutions to parsing `GET_DESCRIPTOR Configuration` and `SET_CONFIGURATION` requests in the following files:
|
||||
|
||||
- `advanced/common/src/get-descriptor-configuration.rs`
|
||||
- `advanced/common/src/set-configuration.rs`
|
||||
- `advanced/common/usb/solution-get-descriptor-configuration.rs`
|
||||
- `advanced/common/usb/solution-set-configuration.rs`
|
||||
|
||||
Each file contains just enough code to parse the request in its name and the `GET_DESCRIPTOR Device` request. So you can refer to `set-configuration.rs` without getting "spoiled" about how to parse the `SET_CONFIGURATION` request.
|
||||
Each file contains just enough code to parse the request in its name and the `GET_DESCRIPTOR Device` and `SET_ADDRESS` requests. So you can refer to `solution-get-descriptor-configuration.rs` without getting "spoiled" about how to parse the `SET_CONFIGURATION` request.
|
||||
|
|
Loading…
Reference in a new issue