Skip to content

Commit 5aa7684

Browse files
committed
Enable specifying the alternate setting for the DFU interface per-target
This works by "padding" the DFU alternate setting with dummy no-op descriptors that occupy all previous alternate setting values. AFAICT while these no-op interfaces are not documented in the USB defined class codes, they don't identify as anything in Linux and cause no driver to be loaded nor any warnings in the system logs, and they should be fine for other operating systems as well. When USB_ALT > 0, the DFU interface appears in the specific alternate setting, which dfu-util does automatically identify and use, alternatively it can be manually targeted with the -a flag. To have USB_ALT be a per-target (optional) property the target.h implementor must load it (since it has the macro value to keep everything compile-time), which is why I've had to split out the descriptor structs into their own file to be preprocessed as part of target_stm32f103.c or target_stm32l1.c. The USB initialization code can then call target_usb_descriptor() to get the pointer to the configuration descriptor defined by the target.
1 parent 087b7c7 commit 5aa7684

5 files changed

Lines changed: 115 additions & 39 deletions

File tree

src/stm32f103/target_stm32f103.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "target.h"
3030
#include "config.h"
3131
#include "backup.h"
32+
#include "usb_descriptor.h"
3233

3334
#ifndef USES_GPIOA
3435
#if (HAVE_USB_PULLUP_CONTROL == 0)
@@ -157,6 +158,10 @@ const usbd_driver* target_usb_init(void) {
157158
return &st_usbfs_v1_usb_driver;
158159
}
159160

161+
const struct usb_config_descriptor* target_usb_descriptor(void) {
162+
return &usb_config;
163+
}
164+
160165
bool target_get_force_bootloader(void) {
161166
bool force = false;
162167
/* Check the RTC backup register */

src/stm32l1/target_stm32l1.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "target.h"
2727
#include "config.h"
2828
#include "backup.h"
29+
#include "usb_descriptor.h"
2930

3031
//#define CMD_FAST_BOOT 0xfa57b007
3132
static const uint32_t CMD_BOOT = 0x544F4F42UL;
@@ -75,6 +76,11 @@ const usbd_driver* target_usb_init(void)
7576
return &st_usbfs_v1_usb_driver;
7677
}
7778

79+
const struct usb_config_descriptor* target_usb_descriptor(void)
80+
{
81+
return &usb_config;
82+
}
83+
7884
/* This implementation will always start in bootloader, unless the app
7985
* has asked it to skipp straight forwards
8086
* You may wish to fill in button handling...

src/target.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
extern void target_clock_setup(void);
2727
extern void target_gpio_setup(void);
2828
extern const usbd_driver* target_usb_init(void);
29+
extern const struct usb_config_descriptor* target_usb_descriptor(void);
2930
extern bool target_get_force_bootloader(void);
3031
extern void target_get_serial_number(char* dest, size_t max_chars);
3132
extern size_t target_get_max_firmware_size(void);

src/usb_conf.c

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -45,44 +45,6 @@ static const struct usb_device_descriptor dev = {
4545
.bNumConfigurations = 1,
4646
};
4747

48-
static const struct usb_interface_descriptor dfu_iface = {
49-
.bLength = USB_DT_INTERFACE_SIZE,
50-
.bDescriptorType = USB_DT_INTERFACE,
51-
.bInterfaceNumber = INTF_DFU,
52-
.bAlternateSetting = 0,
53-
.bNumEndpoints = 0,
54-
.bInterfaceClass = 0xFE,
55-
.bInterfaceSubClass = 1,
56-
.bInterfaceProtocol = 2,
57-
.iInterface = 4,
58-
59-
.endpoint = NULL,
60-
61-
.extra = &dfu_function,
62-
.extralen = sizeof(dfu_function),
63-
};
64-
65-
static const struct usb_interface interfaces[] = {
66-
/* DFU interface */
67-
{
68-
.num_altsetting = 1,
69-
.altsetting = &dfu_iface,
70-
}
71-
};
72-
73-
static const struct usb_config_descriptor config = {
74-
.bLength = USB_DT_CONFIGURATION_SIZE,
75-
.bDescriptorType = USB_DT_CONFIGURATION,
76-
.wTotalLength = 0,
77-
.bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface),
78-
.bConfigurationValue = 1,
79-
.iConfiguration = 0,
80-
.bmAttributes = 0xC0,
81-
.bMaxPower = 0x32,
82-
83-
.interface = interfaces,
84-
};
85-
8648
static const struct usb_device_capability_descriptor* capabilities[] = {
8749
(const struct usb_device_capability_descriptor*)&webusb_platform,
8850
};
@@ -119,7 +81,7 @@ usbd_device* usb_setup(void) {
11981
int num_strings = sizeof(usb_strings)/sizeof(const char*);
12082

12183
const usbd_driver* driver = target_usb_init();
122-
usbd_device* usbd_dev = usbd_init(driver, &dev, &config, &bos,
84+
usbd_device* usbd_dev = usbd_init(driver, &dev, target_usb_descriptor(), &bos,
12385
usb_strings, num_strings,
12486
usbd_control_buffer, sizeof(usbd_control_buffer));
12587

src/usb_descriptor.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2021, Dennis Marttinen
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software
5+
* for any purpose with or without fee is hereby granted, provided
6+
* that the above copyright notice and this permission notice
7+
* appear in all copies.
8+
*
9+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10+
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11+
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12+
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13+
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14+
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15+
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
19+
#ifndef DAPBOOT_USB_DESCRIPTOR_H
20+
#define DAPBOOT_USB_DESCRIPTOR_H
21+
22+
#include "usb_conf.h"
23+
#include "dfu.h"
24+
25+
// Macro to create a dummy (no-op) USB interface descriptor with the given alternate setting
26+
#define ALT_DUMMY(N) { \
27+
.bLength = USB_DT_INTERFACE_SIZE, \
28+
.bDescriptorType = USB_DT_INTERFACE, \
29+
.bInterfaceNumber = INTF_DFU, \
30+
.bAlternateSetting = (N), \
31+
.bNumEndpoints = 0, \
32+
.bInterfaceClass = 0, \
33+
.bInterfaceSubClass = 0, \
34+
.bInterfaceProtocol = 0, \
35+
.iInterface = 0, \
36+
.endpoint = NULL, \
37+
.extra = NULL, \
38+
.extralen = 0, \
39+
},
40+
41+
// Functionality for creating repetitive ALT_DUMMY structs with an increasing count during compile time.
42+
// It doesn't look very nice, but C doesn't allow loops in preprocessor macros, so this needs to be hard-coded.
43+
#define ALT0
44+
#define ALT1 ALT_DUMMY(0)
45+
#define ALT2 ALT1 ALT_DUMMY(1)
46+
#define ALT3 ALT2 ALT_DUMMY(2)
47+
#define ALT4 ALT3 ALT_DUMMY(3)
48+
#define ALT5 ALT4 ALT_DUMMY(4)
49+
#define ALTC(n) ALT##n
50+
#define ALTN(n) ALTC(n)
51+
52+
// The DFU interface doesn't have any special alternate setting by default
53+
#ifndef USB_ALT
54+
#define USB_ALT 0
55+
#endif
56+
57+
static const struct usb_interface_descriptor altsettings[] = {
58+
ALTN(USB_ALT) // Prepend USB_ALT dummy USB interface descriptors to "pad" the real one
59+
{
60+
.bLength = USB_DT_INTERFACE_SIZE,
61+
.bDescriptorType = USB_DT_INTERFACE,
62+
.bInterfaceNumber = INTF_DFU,
63+
.bAlternateSetting = USB_ALT,
64+
.bNumEndpoints = 0,
65+
.bInterfaceClass = 0xFE,
66+
.bInterfaceSubClass = 1,
67+
.bInterfaceProtocol = 2,
68+
.iInterface = 0,
69+
70+
.endpoint = NULL,
71+
72+
.extra = &dfu_function,
73+
.extralen = sizeof(dfu_function),
74+
}
75+
};
76+
77+
// Tracking this is mandatory if exposing multiple altsettings
78+
static uint8_t cur_altsetting = 0;
79+
80+
static const struct usb_interface interfaces[] = {
81+
/* DFU interface */
82+
{
83+
.cur_altsetting = &cur_altsetting,
84+
.num_altsetting = USB_ALT + 1,
85+
.altsetting = (const struct usb_interface_descriptor*)&altsettings,
86+
},
87+
};
88+
89+
static const struct usb_config_descriptor usb_config = {
90+
.bLength = USB_DT_CONFIGURATION_SIZE,
91+
.bDescriptorType = USB_DT_CONFIGURATION,
92+
.wTotalLength = 0,
93+
.bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface),
94+
.bConfigurationValue = 1,
95+
.iConfiguration = 0,
96+
.bmAttributes = 0xC0,
97+
.bMaxPower = 0x32,
98+
99+
.interface = interfaces,
100+
};
101+
102+
#endif //DAPBOOT_USB_DESCRIPTOR_H

0 commit comments

Comments
 (0)