-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
usb: dfu: rework of dfu class implementation #30562
Conversation
b88eab0
to
9b33788
Compare
Minor refactor of USB DFU class implementation, including: . Support for different idVendor and idProduct than during runtime as required by the USB DFU 1.1 specification . Support for all partitions to be available as alternates . Change to DFU descriptors only after USB Reset . Timeout timer added to appDETACH state as required by USB DFU 1.1 specification Signed-off-by: Michael Rosen <[email protected]>
9b33788
to
6a2cff8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please separate it into smaller self-contained commits, describe the reason for the changes in the commits message. Please do not make style fixes.
endif() | ||
endif() | ||
|
||
if(CONFIG_USB_DEVICE_DFU_VID EQUAL 0x2FE3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NACK for CONFIG_USB_DEVICE_DFU_VID, this is no required and not "considered necessary"
This value is only for testing and MUST be configured for USB products." | ||
) | ||
|
||
if(CONFIG_USB_DEVICE_DFU_PID EQUAL 0x101) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also NACK for CONFIG_USB_DEVICE_DFU_PID, "considered necessary" is not a must. Which OS did you observe the problems with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Windows 10, dfu-util 0.9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exact behavior is that the tool cannot find the device in DFU mode after USB reset because of OS-level caching of descriptors (from that Ch2 of the specification, its actually considered "necessary" to change the VID/PID). If you try again, the tool is able to preform DFU operations since the device is doing the correct thing.
|
||
config USB_DEVICE_DFU_PID | ||
hex "USB DFU Product ID" | ||
default 0x0101 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can not assign 0x0101 here. There is a list of assigned PID, samples/subsys/usb/usb_pid.Kconfig. However, I do not think this is necessary at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default PID is assigned in subsys/usb/Kconfig, thats what this is based off of. While a different PID between runtime mode and DFU mode is required by the specification and does have issues on Windows 10 (see other comments), we can have a single VID. I was just providing for maximum flexibility by creating new Kconfig variables for DFU mode descriptors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The one from subsys/usb/Kconfig is leftover before samples/subsys/usb/usb_pid.Kconfig
was inserted. Please assign the value 0xffff for now.
struct usb_string_desription { | ||
struct usb_string_descriptor lang_descr; | ||
struct usb_mfr_descriptor { | ||
uint8_t bLength; | ||
uint8_t bDescriptorType; | ||
uint8_t bString[USB_BSTRING_LENGTH( | ||
CONFIG_USB_DEVICE_MANUFACTURER)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not make style fixes along with other changes. These were not necessary here either.
DT_FOREACH_CHILD(DT_DRV_INST(n), READONLY) | ||
|
||
/** | ||
* @brief Helper function to check if given partition is read-only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no benefit in using Doxygen for internal functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some (not all) internal helper functions in this file had Doxygen comments, should they be added for new helper functions then or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please not for internal functions, I do not know why people do it.
#define DT_EXPAND_CAT(a, b) DT_CAT(a, b) | ||
|
||
#define DEFINE_IF(part) \ | ||
struct usb_if_descriptor DT_EXPAND_CAT(if, DT_FIXED_PARTITION_ID(part)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convert to using DT should be done in separate commit (b). However why must all partitions (mcuboot?, image-0, image-1, image-scratch?, storage?) be exposed? I can not think of a good reason for this. This should remain limited for image-0 or image-0 + image-1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mcuboot is probably never needed, however, its possible any number of additional partitions for file systems and other storage might want to be accessed or preflashed along with an OS image. Thats the motivation behind this change; and exposing all partitions is a simpler solution than trying to figure out which a user would want to expose. An alternative would be to add to device tree to add a flag to determine which partitions want to be exposed, but I didnt adding all that complexity was necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well this implementation depends on image manager and mcuboot. Not only is it not necessary to expose all partitions, it is also confusing for the user and probably not desired by the developer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with generating descriptors for all partitions is that it bloats binary with code that will never be accessed in most cases, for example when user only wants to dfu the image-1 or image-2.
You may, for example, be out of flash on mcuboot partition, when trying to link DFU to mcuboot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True that it does some additional data to the image that would not be used (the changes to the actual code arent significant in terms of code size). However, the additional descriptors really dont cost much memory (9 bytes for interface descriptor, 2 + 2 * strlen(partition-name) bytes for the string descriptors; so assuming a generous 5 extra partitions not used at all by DFU and a length of 15 characters, thats 5*(9 + 2 + 2 * 15) = 205 bytes, which is certainly not zero but compared to whats being added to the image when enabling USB and whats usually required for the image to use mcuboot with USB (32-64KiB) its not a very significant change. If this is a major concern, I would instead propose adding to device tree a marker for which partitions are desired to be accessed over DFU instead of hard limiting it to just image-1 or image-0 in single slot mode.
I agree there are some dependencies on the image manager than I did not entirely remove. Since there is alot of discussion around this topic; I think it would be better to split this PR between the parts fixing issues and the parts implementing the partitions rework (I will split up the commits as @jfischer-no suggested), or is it preferred each fix/commit be a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, do separate commits.
Maybe we should add 'upgreadable' property to elements of 'fixed-partitions' and generate the descriptors only for these partitions that are marked as upgredable
, dfuable
or whatever property there will be.
The ~200 does not seem much but the space in mcuboot partitions starts to get tight and if we can shave something off easily, why not do it?
@@ -368,7 +393,11 @@ static void dfu_flash_write(uint8_t *data, size_t len) | |||
LOG_DBG("flash write done"); | |||
dfu_data.state = dfuMANIFEST_SYNC; | |||
dfu_reset_counters(); | |||
if (boot_request_upgrade(false)) { | |||
/* TODO: Reexamine if there is a nicer way of handling mcuboot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do TODO or remove this comment.
@@ -533,18 +568,10 @@ static int dfu_class_handle_req(struct usb_setup_packet *pSetup, | |||
} | |||
|
|||
if (len) { | |||
const struct flash_area *fa; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change should be done in separate commit (c)
return -EIO; | ||
} | ||
/* Begin detach timeout timer */ | ||
timeout = MIN(pSetup->wValue, CONFIG_USB_DFU_DETACH_TIMEOUT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be done in separate commit (d)
if (dfu_data.state == appDETACH) { | ||
dfu_data.state = dfuIDLE; | ||
|
||
/* Set the DFU mode descriptors to be used after reset */ | ||
dfu_config.usb_device_description = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change should be done in separate commit (e)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I blocked merge until I understood what is changing here.
#define READONLY(part) \ | ||
[DT_FIXED_PARTITION_ID(part)] = DT_PROP(part, read_only), | ||
|
||
#define READONLY_FOREACH_PARTITION(n) \ | ||
DT_FOREACH_CHILD(DT_DRV_INST(n), READONLY) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless the read-only property is really implemented, I see no reason for these to be here.
The idea is OK, but if it is TODO, it should get separate PR with proposed implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it current state, this should be RFC.
/* TODO: Reexamine if there is a nicer way of handling mcuboot | ||
* interface | ||
*/ | ||
if (dfu_data.fa->fa_id == FLASH_AREA_ID(image_1) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is user-case for this change? Currently boot_request_upgrade() is no-op if image_1 dosen't exist (so USB DFU is build in the MCUBoot for this case). It is the operation for dual-bank mode. Dual-XIP support is in progress - but mean also no-op for this function.
dfu_data.status = errWRITE; | ||
dfu_data.state = dfuERROR; | ||
LOG_ERR("This area can not be overwritten"); | ||
LOG_ERR("DFU_DNLOAD area is readonly"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo DFU_DNLOAD -> DFU_DOWNLOAD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the other print messages use DFU_DNLOAD
, should they all be DF_DOWNLOAD
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DFU_DNLOAD is a DFU class request. Maybe:
LOG_ERR("DFU_DNLOAD area is readonly"); | |
LOG_ERR("DFU_DNLOAD write area is readonly"); |
This is not a minor rework. |
As mentioned above, there is a lot more discussion that needs to happen around the partition mapping; however there are a number of minor fixes also in this PR. So, I think its best if we split the PR between those actually minor fixes and the move to using the device tree partition map for USB DFU. As also mentioned, is it preferred by the code owners if those fixes are each a small PR or one PR? |
All the other fixes have been moved to their own PR: #30611 |
My opinion is that rest can be continued here. |
After a bit of thought on this (and coming back to it), I think there are two ways to go about adding the additional functionality while providing the control needed to minimize the number of partitions exposed, both leveraging devicetree:
|
No, I do not think it is a good idea. DT for audio is temporary solution and please do not use it as an example. And we do not obtain "descriptor tables" from from Kconfig. |
@jfischer-no Do you mean for applications to each make their own version of the secondary descriptor table and set a pointer for the dfu implementation to use at runtime? With helper macros that would certainly be one way to do it, but now any application wanting to use DFU would have to include this macro (unless there is a default one in the DFU implementation folder). |
@jfischer-no If its supposed to be user configurable (in code rather than DTS), it gets a bit messy to do while still ensuring the descriptors end up in the readonly section and dont consume RAM; since the descriptors need to change based on what partitions the user selects. As I mentioned above, we can generate them all using macros (not complete):
Another thing to consider with this solution is that any errors generated from a bad partition name or other mistake might be difficult to decipher. But without moving the descriptor into RAM, Im not sure of a better way to handle this. There might be better ways of using the Alternatively, a custom linker section/scripts could be used to setup the descriptors in memory from many different definitions like how device initialization works (I havent looked into this approach in detail). |
Generally, all descriptors are placed in RAM because they are changed at runtime. This is how the stack works. Excuse me, but I do not see any urgent need to change anything in DFU implementation at the moment, especially to add new features, or change how we handle descriptors in current USB stack just because of new feature for DFU. I am in the process of reworking the USB device stack. The handling of the descriptors will change and the application will have more control over them, after that also the USB DFU implementation will be reworked and more abstracted. |
@jfischer-no I didnt notice the descriptors were in RAM, so I apologize for that misunderstanding. The only urgency here was that this is a feature I need for my project (expanded access to other partitions via DFU), and thought it would be a nice feature to upstream in a generic way to enable anyone else who might need it (as well as fix the issues we found when trying to use DFU on Windows that were part of the original PR). I can just use my original technique for my project and we can look at expanding this functionality with the new device stack later; so can close this PR. |
@mrrosen Could you please open a feature request so that it does not get lost? |
This feature can be now tracked here instead. |
To improve the usability and flexibility of the USB DFU class, some fixes and changes were implemented:
read-only
property of the partitions to prevent writing to partitions that arent supposed to be written to (this is actually a more general problem in the flash APIs right now ignoring this property)