usb: dfu: Add support for alternate settings

Add support for alternate settings in order to access additional flash areas.
The DFU example uses the alternate settings as an offset into flash.

Change-Id: I636042c7f71a5d2a2778ec7dd5301a622720107d
Signed-off-by: Adrian Bradianu <adrian.bradianu@windriver.com>
This commit is contained in:
Adrian Bradianu 2016-06-08 15:32:55 +01:00 committed by Inaky Perez-Gonzalez
commit 88656ec981
2 changed files with 117 additions and 21 deletions

View file

@ -56,6 +56,12 @@
#endif /* CONFIG_STDOUT_CONSOLE */
#endif /* CONFIG_USB_DFU_DEBUG */
/* Alternate settings are used to access additional memory segments.
* This example uses the alternate settings as an offset into flash.
*/
#define DFU_FLASH_ADDR (dfu_data.flash_base_addr + \
dfu_data.alt_setting * DFU_ALT_SETTING_OFFSET)
static struct usb_cfg_data dfu_config;
/* Device data structure */
@ -72,6 +78,7 @@ struct dfu_data_t {
uint32_t bytes_sent;
/* Number of bytes received during download */
uint32_t bytes_rcvd;
uint32_t alt_setting; /* DFU alternate setting */
uint8_t buffer[DFU_MAX_XFER_SIZE]; /* DFU data buffer */
enum dfu_state state; /* State of the DFU device */
enum dfu_status status; /* Status of the DFU device */
@ -83,7 +90,7 @@ static struct dfu_data_t dfu_data = {
.status = statusOK,
};
/* Structure representing the global USB description */
/* Structure representing the DFU runtime USB description */
static const uint8_t dfu_runtime_usb_description[] = {
/* Device descriptor */
USB_DEVICE_DESC_SIZE, /* Descriptor size */
@ -101,19 +108,19 @@ static const uint8_t dfu_runtime_usb_description[] = {
LOW_BYTE(BCDDEVICE_RELNUM),
HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */
/* Index of Manufacturer String Descriptor */
0x00,
0x01,
/* Index of Product String Descriptor */
0x00,
0x02,
/* Index of Serial Number String Descriptor */
0x00,
0x03,
DFU_NUM_CONF, /* Number of Possible Configuration */
/* Configuration descriptor */
USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */
USB_CONFIGURATION_DESC, /* Descriptor type */
/* Total length in bytes of data returned */
LOW_BYTE(DFU_CONF_SIZE),
HIGH_BYTE(DFU_CONF_SIZE),
LOW_BYTE(DFU_RUNTIME_CONF_SIZE),
HIGH_BYTE(DFU_RUNTIME_CONF_SIZE),
DFU_NUM_ITF, /* Number of interfaces */
0x01, /* Configuration value */
0x00, /* Index of the Configuration string */
@ -168,7 +175,7 @@ static const uint8_t dfu_runtime_usb_description[] = {
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0
};
/* Structure representing the global USB description */
/* Structure representing the DFU mode USB description */
static const uint8_t dfu_mode_usb_description[] = {
/* Device descriptor */
USB_DEVICE_DESC_SIZE, /* Descriptor size */
@ -186,26 +193,26 @@ static const uint8_t dfu_mode_usb_description[] = {
LOW_BYTE(BCDDEVICE_RELNUM),
HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */
/* Index of Manufacturer String Descriptor */
0x00,
0x01,
/* Index of Product String Descriptor */
0x00,
0x02,
/* Index of Serial Number String Descriptor */
0x00,
0x03,
DFU_NUM_CONF, /* Number of Possible Configuration */
/* Configuration descriptor */
USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */
USB_CONFIGURATION_DESC, /* Descriptor type */
/* Total length in bytes of data returned */
LOW_BYTE(DFU_CONF_SIZE),
HIGH_BYTE(DFU_CONF_SIZE),
LOW_BYTE(DFU_RUNTIME_CONF_SIZE),
HIGH_BYTE(DFU_RUNTIME_CONF_SIZE),
DFU_NUM_ITF, /* Number of interfaces */
0x01, /* Configuration value */
0x00, /* Index of the Configuration string */
USB_CONFIGURATION_ATTRIBUTES, /* Attributes */
MAX_LOW_POWER, /* Max power consumption */
/* Interface descriptor */
/* Interface descriptor, alternate setting 0 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x00, /* Interface index */
@ -215,7 +222,31 @@ static const uint8_t dfu_mode_usb_description[] = {
DFU_INTERFACE_SUBCLASS, /* SubClass */
DFU_MODE_PROTOCOL, /* DFU Runtime Protocol */
/* Index of the Interface String Descriptor */
0x00,
0x04,
/* Interface descriptor, alternate setting 1 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x00, /* Interface index */
0x01, /* Alternate setting */
DFU_NUM_EP, /* Number of Endpoints */
DFU_CLASS, /* Class */
DFU_INTERFACE_SUBCLASS, /* SubClass */
DFU_MODE_PROTOCOL, /* DFU Runtime Protocol */
/* Index of the Interface String Descriptor */
0x05,
/* Interface descriptor, alternate setting 2 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x00, /* Interface index */
0x02, /* Alternate setting */
DFU_NUM_EP, /* Number of Endpoints */
DFU_CLASS, /* Class */
DFU_INTERFACE_SUBCLASS, /* SubClass */
DFU_MODE_PROTOCOL, /* DFU Runtime Protocol */
/* Index of the Interface String Descriptor */
0x06,
/* DFU descriptor */
USB_DFU_DESC_SIZE, /* Descriptor size */
@ -250,7 +281,22 @@ static const uint8_t dfu_mode_usb_description[] = {
/* Serial Number String Descriptor "00.01" */
0x0C,
USB_STRING_DESC,
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0,
/* Interface alternate setting 0 String Descriptor */
0x0E,
USB_STRING_DESC,
'F', 0, 'L', 0, 'A', 0, 'S', 0, 'H', 0, '0', 0,
/* Interface alternate setting 0 String Descriptor */
0x0E,
USB_STRING_DESC,
'F', 0, 'L', 0, 'A', 0, 'S', 0, 'H', 0, '1', 0,
/* Interface alternate setting 0 String Descriptor */
0x0E,
USB_STRING_DESC,
'F', 0, 'L', 0, 'A', 0, 'S', 0, 'H', 0, '2', 0,
};
/**
@ -357,7 +403,7 @@ static int dfu_class_handle_req(struct usb_setup_packet *pSetup,
if (!(dfu_data.bytes_rcvd %
dfu_data.flash_page_size)) {
ret = flash_erase(dfu_data.flash_dev,
dfu_data.flash_base_addr +
DFU_FLASH_ADDR +
dfu_data.bytes_rcvd,
dfu_data.flash_page_size);
DBG("Flash erase\n");
@ -372,7 +418,7 @@ static int dfu_class_handle_req(struct usb_setup_packet *pSetup,
/* Flash write len should be multiple of 4 */
len = (pSetup->wLength + 3) & (~3);
ret = flash_write(dfu_data.flash_dev,
dfu_data.flash_base_addr +
DFU_FLASH_ADDR +
dfu_data.bytes_rcvd,
*data, len);
if (ret) {
@ -427,7 +473,7 @@ static int dfu_class_handle_req(struct usb_setup_packet *pSetup,
if (len) {
ret = flash_read(dfu_data.flash_dev,
dfu_data.flash_base_addr +
DFU_FLASH_ADDR +
dfu_data.bytes_sent,
dfu_data.buffer, len);
if (ret) {
@ -532,12 +578,48 @@ static void dfu_status_cb(enum usb_dc_status_code status)
}
}
/**
* @brief Custom handler for standard ('chapter 9') requests
* in order to catch the SET_INTERFACE request and
* extract the interface alternate setting
*
* @param pSetup Information about the request to execute.
* @param len Size of the buffer.
* @param data Buffer containing the request result.
*
* @return 0 if SET_INTERFACE request, -ENOTSUP otherwise.
*/
static int dfu_custom_handle_req(struct usb_setup_packet *pSetup,
int32_t *data_len, uint8_t **data)
{
if (REQTYPE_GET_RECIP(pSetup->bmRequestType) ==
REQTYPE_RECIP_INTERFACE) {
if (pSetup->bRequest == REQ_SET_INTERFACE) {
DBG("DFU alternate setting %d\n", pSetup->wValue);
if (pSetup->wValue >= DFU_MODE_ALTERNATE_SETTINGS) {
DBG("Invalid DFU alternate setting (%d)\n",
pSetup->wValue);
} else {
dfu_data.alt_setting = pSetup->wValue;
}
*data_len = 0;
return 0;
}
}
/* Not handled by us */
return -ENOTSUP;
}
/* Configuration of the DFU Device send to the USB Driver */
static struct usb_cfg_data dfu_config = {
.usb_device_description = dfu_runtime_usb_description,
.cb_usb_status = dfu_status_cb,
.interface = {
.class_handler = dfu_class_handle_req,
.custom_handler = dfu_custom_handle_req,
.payload_data = dfu_data.buffer,
},
.num_endpoints = DFU_NUM_EP,

View file

@ -107,12 +107,26 @@ enum dfu_state {
dfuERROR,
};
/* Number of DFU interface alternate settings. */
#define DFU_RUNTIME_ALTERNATE_SETTINGS 1
#define DFU_MODE_ALTERNATE_SETTINGS 3
/* Size in bytes of the configuration sent to the Host on
* GetConfiguration() request
* For DFU: CONF + ITF + DFU) -> 27 bytes
* For DFU: CONF + ITF*ALT_SETTINGS + DFU)
*/
#define DFU_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \
USB_INTERFACE_DESC_SIZE + USB_DFU_DESC_SIZE)
#define DFU_MODE_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \
USB_INTERFACE_DESC_SIZE * DFU_RUNTIME_ALTERNATE_SETTINGS + \
USB_DFU_DESC_SIZE)
#define DFU_RUNTIME_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \
USB_INTERFACE_DESC_SIZE * DFU_MODE_ALTERNATE_SETTINGS + \
USB_DFU_DESC_SIZE)
/* Alternate settings are used to access additional memory segments.
* This example uses the alternate settings as an offset into flash.
*/
#define DFU_ALT_SETTING_OFFSET 0x6000
/**
* @brief DFU class driver start routine