Merge pull request #32 from ferrous-systems/tables_to_prose

Turn tables into prose
This commit is contained in:
Jorge Aparicio 2020-07-13 15:46:09 +00:00 committed by GitHub
commit d60478ffaa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 23 deletions

View file

@ -44,12 +44,22 @@ fn on_event(usbd: &USBD, event: Event) {
Event::UsbEp0DataDone => todo!(),
Event::UsbEp0Setup => {
// the BMREQUESTTYPE register contains information about data recipient, transfer type and direction
let bmrequesttype = usbd.bmrequesttype.read().bits() as u8;
// the BREQUEST register stores the type of the current request (e.g. SET_ADDRESS, GET_DESCRIPTOR, ...)
let brequest = usbd.brequest.read().brequest().bits();
// wLength denotes the number of bytes to transfer (if any)
// composed of a high register (WLENGTHH) and a low register (WLENGTHL)
let wlength = (u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8)
| u16::from(usbd.wlengthl.read().wlengthl().bits());
// wIndex is a generic index field whose meaning depends on the request type
// composed of a high register (WINDEXH) and a low register (WINDEXL)
let windex = (u16::from(usbd.windexh.read().windexh().bits()) << 8)
| u16::from(usbd.windexl.read().windexl().bits());
// wValue is a generic paremeter field meaning depends on the request type (e.g. contains the device
// address in SET_ADRESS requests)
// composed of a high register (WVALUEH) and a low register (WVALUEL)
let wvalue = (u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8)
| u16::from(usbd.wvaluel.read().wvaluel().bits());

View file

@ -45,10 +45,20 @@ fn on_event(_usbd: &USBD, event: Event) {
Event::UsbEp0Setup => {
// TODO read USBD registers
// the BMREQUESTTYPE register contains information about data recipient, transfer type and direction
let bmrequesttype: u8 = 0;
// the BREQUEST register stores the type of the current request (e.g. SET_ADDRESS, GET_DESCRIPTOR, ...)
let brequest: u8 = 0;
// wLength denotes the number of bytes to transfer (if any)
// composed of a high register (WLENGTHH) and a low register (WLENGTHL)
let wlength: u16 = 0;
// wIndex is a generic index field whose meaning depends on the request type
// composed of a high register (WINDEXH) and a low register (WINDEXL)
let windex: u16 = 0;
// wValue is a generic paremeter field meaning depends on the request type (e.g. contains the device
// address in SET_ADRESS requests)
// composed of a high register (WVALUEH) and a low register (WVALUEL)
let wvalue: u16 = 0;
log::info!(

View file

@ -1,6 +1,8 @@
# Handling GET_DESCRIPTOR Configuration Requests
When the host issues a GET_DESCRIPTOR request the device needs to respond with the requested configuration descriptor *plus* all the interface and endpoint descriptors associated to that configuration descriptor during the DATA stage.
When the host issues a GET_DESCRIPTOR *Configuration* request the device needs to respond with the requested configuration descriptor *plus* all the interface and endpoint descriptors associated to that configuration descriptor during the DATA stage.
A GET_DESCRIPTOR Configuration request is a GET_DESCRIPTOR request where the descriptor type encoded in the high bit of `wValue` is CONFIGURATION.
We have covered configurations and endpoints but what is an *interface*?
@ -78,4 +80,3 @@ The interface descriptor in the response should contain these fields:
Again, we strongly recommend that you use the `usb2::configuration::Descriptor` and `usb2::interface::Descriptor` abstractions here. Each descriptor instance can be transformed into its byte representation using the `bytes` method -- the method returns an array. To concatenate both arrays you can use an stack-allocated [`heapless::Vec`] buffer. If you haven't the `heapless` crate before you can find example usage in the the `src/bin/vec.rs` file.
[`heapless::Vec`]: https://docs.rs/heapless/0.5.5/heapless/struct.Vec.html

View file

@ -16,19 +16,26 @@ Now modify the `print-descs` program to "open" the device -- this operation is c
## SET_CONFIGURATION
Section 9.4.7, Set Configuration, of the USB spec describes how to handle this request but below you can find a summary:
The SET_CONFIGURATION request is sent by the host to configure the device. Its configuration according to section 9.4.7. of the USB spec is:
- `bmrequesttype` is 0b00000000
- `brequest` is SET_CONFIGURATION
- `wValue` contains the requested configuration value
- `wIndex` and `wLength` are 0, there is no `wData`
To handle a SET_CONFIGURATION, do the following:
- If the device is in the `Default` state, you should stall the endpoint because the operation is not permitted in that state.
- If the device is in the `Address` state, then
- if the requested configuration value is 0 (`None` in the `usb` API) then stay in the `Address` state
- if the requested configuration value is non-zero and valid (was previously reported in a configuration descriptor) then move to the `Configured` state
- if the requested configuration value is not valid then stall the endpoint
- if `wValue` is 0 (`None` in the `usb` API) then stay in the `Address` state
- if `wValue` is non-zero and valid (was previously reported in a configuration descriptor) then move to the `Configured` state
- if `wValue` is not valid then stall the endpoint
- If the device is in the `Configured` state, then
- if the requested configuration value is 0 (`None` in the `usb` API) then return to the `Address` state
- if the requested configuration value is non-zero and valid (was previously reported in a configuration descriptor) then move to the `Configured` state with the new configuration value
- if the requested configuration value is not valid then stall the endpoint
- If the device is in the `Configured` state, then read the requested configuration value from `wValue`
- if `wValue` is 0 (`None` in the `usb` API) then return to the `Address` state
- 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.
This is done by writing 1 to the TASKS_EP0STATUS register.

View file

@ -2,18 +2,25 @@
> This request should come right after the `GET_DESCRIPTOR Device` request if you're using Linux, or be the first request sent to the device by Mac OS.
Section 9.4.6, Set Address, describes how to handle this request but below you can find a summary:
A SET_ADDRESS request has the following fields as defined by Section 9.4.6 Set Address of the USB spec:
- `bmrequesttype` is 0b00000000
- `brequest` is SET_ADDRESS
- `wValue` contains the address to be used for all subsequent accesses
- `wIndex` and `wLength` are 0, there is no `wData`
It should be handled as follows:
- If the device is in the `Default` state, then
- if the requested address was `0` (`None` in the `usb` API) then the device should stay in the `Default` state
- if the requested address stored in `wValue` was `0` (`None` in the `usb` API) then the device should stay in the `Default` state
- otherwise the device should move to the `Address` state
- If the device is in the `Address` state, then
- if the requested address was `0` (`None` in the `usb` API) then the device should return to the `Default` state
- if the requested address stored in `wValue` was `0` (`None` in the `usb` API) then the device should return to the `Default` state
- otherwise the device should remain in the `Address` state but start using the new address
- If the device is in the `Configured` state this request results in "unspecified" behavior according to the USB specification. You should stall the endpoint in this case.
> Note: According to the USB specification the device needs to respond to this request with a STATUS stage -- the DATA stage is omitted. The nRF52840 USBD peripheral will automatically issue the STATUS stage and switch to listening to the requested address (see the USBADDR register) so no interaction with the USBD peripheral is required for this request.
>
> For more details, read the introduction of section 6.35.9 of the nRF52840 Product Specification 1.0 (pages 486 and 487).
> For more details, read the introduction of section 6.35.9 of the nRF52840 Product Specification 1.0.

View file

@ -1,20 +1,29 @@
# 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 following registers: BMREQUESTTYPE, BREQUEST, WVALUE{L,H}, WINDEX{L,H} and WLENGTH{L,H}. These registers are documented in sections 6.35.13.31 to 6.35.13.38 of the [nRF52840 Product Specification][nrf product spec].
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].
[nrf product spec]: https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.1.pdf
The format of this setup data is documented in section 9.3 of the USB specification. Your next task is to parse it. We will start with the GET_DESCRIPTOR request, which is described in detail in section 9.4.3 of the USB specification. All the constants you will need are described in Tables 9-3, 9-4 and 9-5.
> NOTE: If you'd like to learn more, take a look at Section 9.4, Standard Descriptor Requests, of the USB specification.
When you need to write some `no_std` code that does not involve device-specific I/O you should consider writing it as a separate crate. This way, you can test it on your development machine (e.g. `x86_64`) using the standard `cargo test` functionality.
So that's what we'll do here. In `advanced/common/usb/lib.rs` you'll find starter code for writing a `no_std` SETUP data parser. The starter code contains some unit tests; you can run them with `cargo test` (from within the `usb` folder) or you can use Rust Analyzer's "Test" button in VS code.
The definition of `Descriptor::Configuration` as well as the associated test has been "commented out" using an `#[cfg(TODO)]` attribute because it is not handled by the firmware yet. Delete the `#[cfg(TODO)]` so that the unit tests can access it. This pattern is used for enum members and test functions throughout this workshop, so keep it in mind should you see it again.
Now, proceed as follows:
Your task now is to parse the data of this SETUP stage. We will start with the GET_DESCRIPTOR request, which is described in detail in section 9.4.3 of the USB specification. All the constants you will need are described in Tables 9-3, 9-4 and 9-5.
The fields of a GET_DESCRIPTOR request are as follows:
- `bmRequestType` is 0b10000000
- `bRequest` is GET_DESCRIPTOR
- the high byte of `wValue` contains the descriptor type, whereas the low byte contains the descriptor index
- `wIndex` is set to 0 for our purposes
You will also find this information in the `// TODO implement ...` comment in the `Request::parse()` function of `lib.rs` file.
> NOTE: If you'd like to learn more, take a look at Section 9.4.3 Get Descriptor of the USB specification.
To complete the task, proceed like this:
1. **Parse GET_DESCRIPTOR requests:**
Modify `Request::parse()` in `advanced/common/usb/src/lib.rs` to recognize a GET_DESCRIPTOR request so that the `get_descriptor_device` test passes. Note that the parser already handles SET_ADDRESS requests.
@ -47,4 +56,3 @@ INFO:usb_2 -- Goal reached; move to the next section
You can find a solution to step 1. in `advanced/common/usb/get-descriptor-device.rs`.
You can find a solution to step 2. in `advanced/firmware/src/bin/usb-2-solution.rs`.

View file

@ -2,8 +2,8 @@
After responding to the `GET_DESCRIPTOR Device` request the host will start sending different requests. The parser in `common/usb` will need to be updated to handle these requests:
1. `GET_DESCRIPTOR Configuration`, see section 9.4.3 of the USB spec
2. `SET_CONFIGURATION`, see section 9.4.7 of the USB spec -- this request is likely to only be observed on Linux during enumeration
1. `GET_DESCRIPTOR Configuration`, see section [Handling GET_DESCRIPTOR Configuration Requests](#handling-get_descriptor-configuration-requests)
2. `SET_CONFIGURATION`, see section [SET_CONFIGURATION](#set_configuration) of this course material
The starter `common/usb` code contains unit tests for these other requests as well as extra `Request` variants for these requests. All of them have been commented out using a `#[cfg(TODO)]` attribute which you can remove once you need any new variant or new unit test.