subsys: usb: rework USB DFU class driver
This patch moves USB DFU class driver to subsys/usb/class. For the first the USB DFU class driver depends on DFU image manager and partition layout and is limited to use as an application for the bootloader. The driver fetches the information about the flash, erase block size, write block size and partitions offset from the DT now. The driver has two interfaces associated with the two partitions "SLOT-0" and "SLOT-1". The "SLOT-0" can only be read. In the following work the class driver can be extended so that it can be used from the bootloader and update a flash region directly from the bootloader. Signed-off-by: Johann Fischer <j.fischer@phytec.de>
This commit is contained in:
parent
46700eaa65
commit
b2ca5ee5bc
110
samples/subsys/usb/dfu/README.rst
Normal file
110
samples/subsys/usb/dfu/README.rst
Normal file
|
@ -0,0 +1,110 @@
|
|||
.. _usb_dfu:
|
||||
|
||||
USB DFU Sample Application
|
||||
##########################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample app demonstrates use of a USB DFU Class driver provided
|
||||
by the Zephyr project.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
This project requires an USB device driver. Currently, the USB DFU
|
||||
class provided by the Zephyr project depends on DFU image manager and
|
||||
partition layout. Refer to :ref:`flash_partitions` for details about
|
||||
partition layout. You SoC must run MCUboot as the stage 1 bootloader.
|
||||
This sample is built as an application for the MCUboot bootloader.
|
||||
|
||||
Building and Testing
|
||||
********************
|
||||
|
||||
Building and signing the application
|
||||
====================================
|
||||
|
||||
This sample can be built in the usual way (see :ref:`build_an_application`
|
||||
for more details) and flashed with regular flash tools, but will need
|
||||
to be loaded at the offset of SLOT-0.
|
||||
|
||||
Application images (such as this sample) must be signed.
|
||||
Use the ``scripts/imagetool.py`` script from the `MCUboot GitHub repo`_
|
||||
to sign the image. (See the `Using MCUboot with Zephyr`_ documentation for
|
||||
details.)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
~/src/mcuboot/scripts/imgtool.py sign \
|
||||
--key ~/src/mcuboot/root-rsa-2048.pem \
|
||||
--header-size 0x200 \
|
||||
--align 8 \
|
||||
--version 1.2 \
|
||||
--included-header \
|
||||
./zephyr/zephyr.bin \
|
||||
signed-zephyr.bin
|
||||
|
||||
Build and flash MCUboot bootloader for Zephyr project as it is described in
|
||||
the `Using MCUboot with Zephyr`_ documentation. Then build, sign and flash
|
||||
the USB DFU sample at the offset of SLOT-0.
|
||||
|
||||
Build and sign a second application image e.g. :ref:`hello_world`,
|
||||
which will be used as an image for the update.
|
||||
Do not forget to enable the required MCUboot Kconfig option (as described
|
||||
in :ref:`mcuboot`) by adding the following line to
|
||||
:file:`samples/hello_world/prj.conf`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
CONFIG_BOOTLOADER_MCUBOOT=y
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
The Linux ``dfu-util`` tool can be used to backup or update the application
|
||||
image.
|
||||
|
||||
Use the following command to backup the SLOT-0 image:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
dfu-util --alt 0 --upload slot0_backup.bin
|
||||
|
||||
Use the following command to update the application:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
dfu-util --alt 1 --download signed-hello.bin
|
||||
|
||||
Reset the SoC. MCUboot boot will swap the images and boot the new application,
|
||||
showing this output to the console:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
***** Booting Zephyr OS v1.1.0-65-g4ec7f76 *****
|
||||
[MCUBOOT] [INF] main: Starting bootloader
|
||||
[MCUBOOT] [INF] boot_status_source: Image 0: magic=unset, copy_done=0xff, image_ok=0xff
|
||||
[MCUBOOT] [INF] boot_status_source: Scratch: magic=unset, copy_done=0xe, image_ok=0xff
|
||||
[MCUBOOT] [INF] boot_status_source: Boot source: slot 0
|
||||
[MCUBOOT] [INF] boot_swap_type: Swap type: test
|
||||
[MCUBOOT] [INF] main: Bootloader chainload address offset: 0x20000
|
||||
[MCUBOOT] [INF] main: Jumping to the first image slot0
|
||||
***** Booting Zephyr OS v1.11.0-830-g9df01813c4 *****
|
||||
Hello World! arm
|
||||
|
||||
Reset the SoC again and MCUboot should revert the images and boot
|
||||
USB DFU sample, showing this output to the console:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
***** Booting Zephyr OS v1.1.0-65-g4ec7f76 *****
|
||||
[MCUBOOT] [INF] main: Starting bootloader
|
||||
[MCUBOOT] [INF] boot_status_source: Image 0: magic=good, copy_done=0x1, image_ok=0xff
|
||||
[MCUBOOT] [INF] boot_status_source: Scratch: magic=unset, copy_done=0xe, image_ok=0xff
|
||||
[MCUBOOT] [INF] boot_status_source: Boot source: none
|
||||
[MCUBOOT] [INF] boot_swap_type: Swap type: revert
|
||||
[MCUBOOT] [INF] main: Bootloader chainload address offset: 0x20000
|
||||
***** Booting Zephyr OS v1.11.0-830-g9df01813c4 *****
|
||||
|
||||
.. _MCUboot GitHub repo: https://github.com/runtimeco/mcuboot
|
||||
.. _Using MCUboot with Zephyr: https://mcuboot.com/mcuboot/readme-zephyr.html
|
|
@ -4,8 +4,11 @@ CONFIG_ARC_INIT=n
|
|||
CONFIG_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_DEVICE_PRODUCT="Zephyr DFU sample"
|
||||
CONFIG_USB_DFU_CLASS=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_SOC_FLASH_QMSI=y
|
||||
CONFIG_IMG_MANAGER=y
|
||||
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||
CONFIG_SYS_LOG=y
|
||||
CONFIG_SYS_LOG_USB_DRIVER_LEVEL=1
|
||||
CONFIG_SYS_LOG_USB_DEVICE_LEVEL=1
|
||||
CONFIG_BOOTLOADER_MCUBOOT=y
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
sample:
|
||||
name: DFU over USB
|
||||
name: USB DFU sample
|
||||
tests:
|
||||
test_x86:
|
||||
test:
|
||||
platform_whitelist: nrf52840_pca10056
|
||||
depends_on: usb_device
|
||||
platform_whitelist: quark_se_c1000_devboard
|
||||
tags: usb
|
||||
|
|
|
@ -1,51 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
* Copyright (c) 2017 Phytec Messtechnik GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Sample app for DFU class driver
|
||||
*
|
||||
* This sample app implements a DFU class driver. It does not perform an actual
|
||||
* firmware upgrade instead it allows user to upload a file at a predetermined
|
||||
* flash address or to download the content from that flash address.
|
||||
*/
|
||||
/* Sample app for USB DFU class driver. */
|
||||
|
||||
#include <string.h>
|
||||
#include <zephyr.h>
|
||||
#include "usb_dfu.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef CONFIG_SOC_QUARK_SE_C1000
|
||||
#define DFU_FLASH_DEVICE "QUARK_FLASH"
|
||||
/* Unused flash area to test DFU class driver */
|
||||
#define DFU_FLASH_TEST_ADDR (0x40030000 + 0x10000)
|
||||
#define DFU_FLASH_PAGE_SIZE (2048)
|
||||
#define DFU_FLASH_UPLOAD_SIZE (0x6000)
|
||||
#else
|
||||
#error "Unsupported board"
|
||||
#endif
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
void main(void)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
|
||||
printf("DFU Test Application\n");
|
||||
|
||||
dev = device_get_binding(DFU_FLASH_DEVICE);
|
||||
if (!dev) {
|
||||
printf("Flash device not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dfu_start(dev,
|
||||
DFU_FLASH_TEST_ADDR,
|
||||
DFU_FLASH_PAGE_SIZE,
|
||||
DFU_FLASH_UPLOAD_SIZE);
|
||||
|
||||
while (1) {
|
||||
/* do nothing */
|
||||
}
|
||||
/* Nothing to be done other than the selecting appropriate build
|
||||
* config options. Use dfu-util to update the device.
|
||||
*/
|
||||
SYS_LOG_INF("This device supports USB DFU class.\n");
|
||||
}
|
||||
|
|
|
@ -1,666 +0,0 @@
|
|||
/*******************************************************************************
|
||||
*
|
||||
* Copyright(c) 2015,2016 Intel Corporation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief DFU class driver
|
||||
*
|
||||
* USB DFU device class driver
|
||||
*
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <flash.h>
|
||||
#include <usb/usb_device.h>
|
||||
#include <logging/sys_log.h>
|
||||
#include "usb_dfu.h"
|
||||
|
||||
/* 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)
|
||||
|
||||
/* Misc. macros */
|
||||
#define LOW_BYTE(x) ((x) & 0xFF)
|
||||
#define HIGH_BYTE(x) ((x) >> 8)
|
||||
|
||||
static struct usb_cfg_data dfu_config;
|
||||
|
||||
/* Device data structure */
|
||||
struct dfu_data_t {
|
||||
/* Flash device to read/write data from/to */
|
||||
struct device *flash_dev;
|
||||
/* Flash base address to write/read to/from */
|
||||
u32_t flash_base_addr;
|
||||
/* Flash page size */
|
||||
u32_t flash_page_size;
|
||||
/* Size of the upload transfer */
|
||||
u32_t flash_upload_size;
|
||||
/* Number of bytes sent during upload */
|
||||
u32_t bytes_sent;
|
||||
/* Number of bytes received during download */
|
||||
u32_t bytes_rcvd;
|
||||
u32_t alt_setting; /* DFU alternate setting */
|
||||
u8_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 */
|
||||
u16_t block_nr; /* DFU block number */
|
||||
};
|
||||
|
||||
static struct dfu_data_t dfu_data = {
|
||||
.state = appIDLE,
|
||||
.status = statusOK,
|
||||
};
|
||||
|
||||
/* Structure representing the DFU runtime USB description */
|
||||
static const u8_t dfu_runtime_usb_description[] = {
|
||||
/* Device descriptor */
|
||||
USB_DEVICE_DESC_SIZE, /* Descriptor size */
|
||||
USB_DEVICE_DESC, /* Descriptor type */
|
||||
LOW_BYTE(USB_1_1),
|
||||
HIGH_BYTE(USB_1_1), /* USB version in BCD format */
|
||||
0x00, /* Class - Interface specific */
|
||||
0x00, /* SubClass - Interface specific */
|
||||
0x00, /* Protocol - Interface specific */
|
||||
MAX_PACKET_SIZE0, /* EP0 Max Packet Size */
|
||||
LOW_BYTE(CONFIG_USB_DEVICE_VID),
|
||||
HIGH_BYTE(CONFIG_USB_DEVICE_VID),/* Vendor Id */
|
||||
LOW_BYTE(CONFIG_USB_DEVICE_PID),
|
||||
HIGH_BYTE(CONFIG_USB_DEVICE_PID),/* Product Id */
|
||||
LOW_BYTE(BCDDEVICE_RELNUM),
|
||||
HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */
|
||||
/* Index of Manufacturer String Descriptor */
|
||||
0x01,
|
||||
/* Index of Product String Descriptor */
|
||||
0x02,
|
||||
/* Index of Serial Number String Descriptor */
|
||||
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_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 */
|
||||
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
|
||||
USB_INTERFACE_DESC, /* Descriptor type */
|
||||
0x00, /* Interface index */
|
||||
0x00, /* Alternate setting */
|
||||
DFU_NUM_EP, /* Number of Endpoints */
|
||||
DFU_CLASS, /* Class */
|
||||
DFU_INTERFACE_SUBCLASS, /* SubClass */
|
||||
DFU_RUNTIME_PROTOCOL, /* DFU Runtime Protocol */
|
||||
/* Index of the Interface String Descriptor */
|
||||
0x00,
|
||||
|
||||
/* DFU descriptor */
|
||||
USB_DFU_DESC_SIZE, /* Descriptor size */
|
||||
USB_DFU_FUNCTIONAL_DESC, /* Descriptor type DFU: Functional */
|
||||
DFU_ATTR_CAN_DNLOAD |
|
||||
DFU_ATTR_CAN_UPLOAD |
|
||||
DFU_ATTR_MANIFESTATION_TOLERANT,/* DFU attributes */
|
||||
LOW_BYTE(DFU_DETACH_TIMEOUT),
|
||||
HIGH_BYTE(DFU_DETACH_TIMEOUT), /* wDetachTimeOut */
|
||||
LOW_BYTE(DFU_MAX_XFER_SIZE),
|
||||
HIGH_BYTE(DFU_MAX_XFER_SIZE), /* wXferSize - 512bytes */
|
||||
0x11, 0, /* DFU Version */
|
||||
|
||||
/* String descriptor language, only one, so min size 4 bytes.
|
||||
* 0x0409 English(US) language code used.
|
||||
*/
|
||||
USB_STRING_DESC_SIZE, /* Descriptor size */
|
||||
USB_STRING_DESC, /* Descriptor type */
|
||||
0x09,
|
||||
0x04,
|
||||
|
||||
/* Manufacturer String Descriptor "Intel" */
|
||||
0x0C,
|
||||
USB_STRING_DESC,
|
||||
'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0,
|
||||
|
||||
/* Product String Descriptor "DFU-Dev" */
|
||||
0x10,
|
||||
USB_STRING_DESC,
|
||||
'D', 0, 'F', 0, 'U', 0, '-', 0, 'D', 0, 'e', 0, 'v', 0,
|
||||
|
||||
/* Serial Number String Descriptor "00.01" */
|
||||
0x0C,
|
||||
USB_STRING_DESC,
|
||||
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0
|
||||
};
|
||||
|
||||
/* Structure representing the DFU mode USB description */
|
||||
static const u8_t dfu_mode_usb_description[] = {
|
||||
/* Device descriptor */
|
||||
USB_DEVICE_DESC_SIZE, /* Descriptor size */
|
||||
USB_DEVICE_DESC, /* Descriptor type */
|
||||
LOW_BYTE(USB_1_1),
|
||||
HIGH_BYTE(USB_1_1), /* USB version in BCD format */
|
||||
0x00, /* Class - Interface specific */
|
||||
0x00, /* SubClass - Interface specific */
|
||||
0x00, /* Protocol - Interface specific */
|
||||
MAX_PACKET_SIZE0, /* EP0 Max Packet Size */
|
||||
LOW_BYTE(CONFIG_USB_DEVICE_VID),
|
||||
HIGH_BYTE(CONFIG_USB_DEVICE_VID),/* Vendor Id */
|
||||
LOW_BYTE(CONFIG_USB_DEVICE_PID),
|
||||
HIGH_BYTE(CONFIG_USB_DEVICE_PID),/* Product Id */
|
||||
LOW_BYTE(BCDDEVICE_RELNUM),
|
||||
HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */
|
||||
/* Index of Manufacturer String Descriptor */
|
||||
0x01,
|
||||
/* Index of Product String Descriptor */
|
||||
0x02,
|
||||
/* Index of Serial Number String Descriptor */
|
||||
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_MODE_CONF_SIZE),
|
||||
HIGH_BYTE(DFU_MODE_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, alternate setting 0 */
|
||||
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
|
||||
USB_INTERFACE_DESC, /* Descriptor type */
|
||||
0x00, /* Interface index */
|
||||
0x00, /* 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 */
|
||||
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 */
|
||||
USB_DFU_FUNCTIONAL_DESC, /* Descriptor type DFU: Functional */
|
||||
DFU_ATTR_CAN_DNLOAD |
|
||||
DFU_ATTR_CAN_UPLOAD |
|
||||
DFU_ATTR_MANIFESTATION_TOLERANT,/* DFU attributes */
|
||||
LOW_BYTE(DFU_DETACH_TIMEOUT),
|
||||
HIGH_BYTE(DFU_DETACH_TIMEOUT), /* wDetachTimeOut */
|
||||
LOW_BYTE(DFU_MAX_XFER_SIZE),
|
||||
HIGH_BYTE(DFU_MAX_XFER_SIZE), /* wXferSize - 512bytes */
|
||||
0x11, 0, /* DFU Version */
|
||||
|
||||
/* String descriptor language, only one, so min size 4 bytes.
|
||||
* 0x0409 English(US) language code used.
|
||||
*/
|
||||
USB_STRING_DESC_SIZE, /* Descriptor size */
|
||||
USB_STRING_DESC, /* Descriptor type */
|
||||
0x09,
|
||||
0x04,
|
||||
|
||||
/* Manufacturer String Descriptor "Intel" */
|
||||
0x0C,
|
||||
USB_STRING_DESC,
|
||||
'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0,
|
||||
|
||||
/* Product String Descriptor "DFU-Dev" */
|
||||
0x10,
|
||||
USB_STRING_DESC,
|
||||
'D', 0, 'F', 0, 'U', 0, '-', 0, 'D', 0, 'e', 0, 'v', 0,
|
||||
|
||||
/* Serial Number String Descriptor "00.01" */
|
||||
0x0C,
|
||||
USB_STRING_DESC,
|
||||
'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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper function to check if in DFU app state.
|
||||
*
|
||||
* @return true if app state, false otherwise.
|
||||
*/
|
||||
static bool dfu_check_app_state(void)
|
||||
{
|
||||
if (dfu_data.state == appIDLE ||
|
||||
dfu_data.state == appDETACH) {
|
||||
dfu_data.state = appIDLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to reset DFU internal counters.
|
||||
*/
|
||||
static void dfu_reset_counters(void)
|
||||
{
|
||||
dfu_data.bytes_sent = 0;
|
||||
dfu_data.bytes_rcvd = 0;
|
||||
dfu_data.block_nr = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler called for DFU Class requests not handled by the USB stack.
|
||||
*
|
||||
* @param pSetup Information about the request to execute.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
*
|
||||
* @return 0 on success, negative errno code on fail.
|
||||
*/
|
||||
static int dfu_class_handle_req(struct usb_setup_packet *pSetup,
|
||||
s32_t *data_len, u8_t **data)
|
||||
{
|
||||
int ret;
|
||||
u32_t len, bytes_left;
|
||||
|
||||
switch (pSetup->bRequest) {
|
||||
case DFU_GETSTATUS:
|
||||
SYS_LOG_DBG("DFU_GETSTATUS: status %d, state %d",
|
||||
dfu_data.status, dfu_data.state);
|
||||
|
||||
if (dfu_data.state == dfuMANIFEST_SYNC)
|
||||
dfu_data.state = dfuIDLE;
|
||||
|
||||
(*data)[0] = dfu_data.status;
|
||||
(*data)[1] = 0;
|
||||
(*data)[2] = 1;
|
||||
(*data)[3] = 0;
|
||||
(*data)[4] = dfu_data.state;
|
||||
(*data)[5] = 0;
|
||||
*data_len = 6;
|
||||
break;
|
||||
|
||||
case DFU_GETSTATE:
|
||||
SYS_LOG_DBG("DFU_GETSTATE");
|
||||
(*data)[0] = dfu_data.state;
|
||||
*data_len = 1;
|
||||
break;
|
||||
|
||||
case DFU_ABORT:
|
||||
SYS_LOG_DBG("DFU_ABORT");
|
||||
|
||||
if (dfu_check_app_state())
|
||||
return -EINVAL;
|
||||
|
||||
dfu_reset_counters();
|
||||
dfu_data.state = dfuIDLE;
|
||||
dfu_data.status = statusOK;
|
||||
break;
|
||||
|
||||
case DFU_CLRSTATUS:
|
||||
SYS_LOG_DBG("DFU_CLRSTATUS");
|
||||
|
||||
if (dfu_check_app_state())
|
||||
return -EINVAL;
|
||||
|
||||
dfu_data.state = dfuIDLE;
|
||||
dfu_data.status = statusOK;
|
||||
break;
|
||||
|
||||
case DFU_DNLOAD:
|
||||
SYS_LOG_DBG("DFU_DNLOAD block %d, len %d, state %d",
|
||||
pSetup->wValue, pSetup->wLength, dfu_data.state);
|
||||
|
||||
if (dfu_check_app_state())
|
||||
return -EINVAL;
|
||||
|
||||
switch (dfu_data.state) {
|
||||
case dfuIDLE:
|
||||
dfu_reset_counters();
|
||||
SYS_LOG_DBG("DFU_DNLOAD start");
|
||||
case dfuDNLOAD_IDLE:
|
||||
if (pSetup->wLength != 0) {
|
||||
/* Download has started */
|
||||
dfu_data.state = dfuDNLOAD_IDLE;
|
||||
|
||||
if (!(dfu_data.bytes_rcvd %
|
||||
dfu_data.flash_page_size)) {
|
||||
ret = flash_erase(dfu_data.flash_dev,
|
||||
DFU_FLASH_ADDR +
|
||||
dfu_data.bytes_rcvd,
|
||||
dfu_data.flash_page_size);
|
||||
SYS_LOG_DBG("Flash erase");
|
||||
if (ret) {
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errERASE;
|
||||
SYS_LOG_DBG("DFU flash erase error, "
|
||||
"ret %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Flash write len should be multiple of 4 */
|
||||
len = (pSetup->wLength + 3) & (~3);
|
||||
ret = flash_write(dfu_data.flash_dev,
|
||||
DFU_FLASH_ADDR +
|
||||
dfu_data.bytes_rcvd,
|
||||
*data, len);
|
||||
if (ret) {
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errWRITE;
|
||||
SYS_LOG_DBG("DFU flash write error, ret %d",
|
||||
ret);
|
||||
} else
|
||||
dfu_data.bytes_rcvd += pSetup->wLength;
|
||||
} else {
|
||||
/* Download completed */
|
||||
dfu_data.state = dfuMANIFEST_SYNC;
|
||||
dfu_reset_counters();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_DBG("DFU_DNLOAD wrong state %d", dfu_data.state);
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errUNKNOWN;
|
||||
dfu_reset_counters();
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case DFU_UPLOAD:
|
||||
SYS_LOG_DBG("DFU_UPLOAD block %d, len %d, state %d",
|
||||
pSetup->wValue, pSetup->wLength, dfu_data.state);
|
||||
|
||||
if (dfu_check_app_state())
|
||||
return -EINVAL;
|
||||
|
||||
switch (dfu_data.state) {
|
||||
case dfuIDLE:
|
||||
dfu_reset_counters();
|
||||
SYS_LOG_DBG("DFU_UPLOAD start");
|
||||
case dfuUPLOAD_IDLE:
|
||||
if (!pSetup->wLength ||
|
||||
dfu_data.block_nr != pSetup->wValue) {
|
||||
SYS_LOG_DBG("DFU_UPLOAD block %d, expected %d, "
|
||||
"len %d", pSetup->wValue,
|
||||
dfu_data.block_nr, pSetup->wLength);
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errUNKNOWN;
|
||||
break;
|
||||
}
|
||||
/* Upload in progress */
|
||||
bytes_left = dfu_data.flash_upload_size -
|
||||
dfu_data.bytes_sent;
|
||||
if (bytes_left < pSetup->wLength)
|
||||
len = bytes_left;
|
||||
else
|
||||
len = pSetup->wLength;
|
||||
|
||||
if (len) {
|
||||
ret = flash_read(dfu_data.flash_dev,
|
||||
DFU_FLASH_ADDR +
|
||||
dfu_data.bytes_sent,
|
||||
dfu_data.buffer, len);
|
||||
if (ret) {
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errFILE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*data_len = len;
|
||||
*data = dfu_data.buffer;
|
||||
|
||||
dfu_data.bytes_sent += len;
|
||||
dfu_data.block_nr++;
|
||||
|
||||
if (dfu_data.bytes_sent == dfu_data.flash_upload_size &&
|
||||
len < pSetup->wLength) {
|
||||
/* Upload completed when a
|
||||
* short packet is received
|
||||
*/
|
||||
*data_len = 0;
|
||||
dfu_data.state = dfuIDLE;
|
||||
} else
|
||||
dfu_data.state = dfuUPLOAD_IDLE;
|
||||
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_DBG("DFU_UPLOAD wrong state %d", dfu_data.state);
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errUNKNOWN;
|
||||
dfu_reset_counters();
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case DFU_DETACH:
|
||||
SYS_LOG_DBG("DFU_DETACH timeout %d, state %d",
|
||||
pSetup->wValue, dfu_data.state);
|
||||
|
||||
if (dfu_data.state != appIDLE) {
|
||||
dfu_data.state = appIDLE;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Move to appDETACH state */
|
||||
dfu_data.state = appDETACH;
|
||||
|
||||
/* We should start a timer here but in order to
|
||||
* keep things simple and do not increase the size
|
||||
* we rely on the host to get us out of the appATTACHED
|
||||
* state if needed.
|
||||
*/
|
||||
|
||||
/* Set the DFU mode descriptors to be used after reset */
|
||||
dfu_config.usb_device_description = dfu_mode_usb_description;
|
||||
if (usb_set_config(&dfu_config) != 0) {
|
||||
SYS_LOG_DBG("usb_set_config failed in DFU_DETACH");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_DBG("DFU UNKNOWN STATE: %d", pSetup->bRequest);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback used to know the USB connection status
|
||||
*
|
||||
* @param status USB device status code.
|
||||
*
|
||||
* @return N/A.
|
||||
*/
|
||||
static void dfu_status_cb(enum usb_dc_status_code status, u8_t *param)
|
||||
{
|
||||
ARG_UNUSED(param);
|
||||
|
||||
/* Check the USB status and do needed action if required */
|
||||
switch (status) {
|
||||
case USB_DC_ERROR:
|
||||
SYS_LOG_DBG("USB device error");
|
||||
break;
|
||||
case USB_DC_RESET:
|
||||
SYS_LOG_DBG("USB device reset detected, state %d", dfu_data.state);
|
||||
if (dfu_data.state == appDETACH) {
|
||||
dfu_data.state = dfuIDLE;
|
||||
}
|
||||
break;
|
||||
case USB_DC_CONNECTED:
|
||||
SYS_LOG_DBG("USB device connected");
|
||||
break;
|
||||
case USB_DC_CONFIGURED:
|
||||
SYS_LOG_DBG("USB device configured");
|
||||
break;
|
||||
case USB_DC_DISCONNECTED:
|
||||
SYS_LOG_DBG("USB device disconnected");
|
||||
break;
|
||||
case USB_DC_SUSPEND:
|
||||
SYS_LOG_DBG("USB device supended");
|
||||
break;
|
||||
case USB_DC_RESUME:
|
||||
SYS_LOG_DBG("USB device resumed");
|
||||
break;
|
||||
case USB_DC_UNKNOWN:
|
||||
default:
|
||||
SYS_LOG_DBG("USB unknown state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
s32_t *data_len, u8_t **data)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
|
||||
if (REQTYPE_GET_RECIP(pSetup->bmRequestType) ==
|
||||
REQTYPE_RECIP_INTERFACE) {
|
||||
if (pSetup->bRequest == REQ_SET_INTERFACE) {
|
||||
SYS_LOG_DBG("DFU alternate setting %d", pSetup->wValue);
|
||||
|
||||
if (pSetup->wValue >= DFU_MODE_ALTERNATE_SETTINGS) {
|
||||
SYS_LOG_DBG("Invalid DFU alternate setting (%d)",
|
||||
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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DFU class driver start routine
|
||||
*
|
||||
* @param flash_dev Flash device driver to be used.
|
||||
* @param flash_base_addr Flash address to write/read to/from.
|
||||
* @param flash_page_size Flash page size.
|
||||
* @param flash_upload_size Flash upload data size.
|
||||
*
|
||||
* @return N/A.
|
||||
*/
|
||||
int dfu_start(struct device *flash_dev, u32_t flash_base_addr,
|
||||
u32_t flash_page_size, u32_t flash_upload_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!flash_dev)
|
||||
return -EINVAL;
|
||||
|
||||
dfu_data.flash_dev = flash_dev;
|
||||
dfu_data.flash_base_addr = flash_base_addr;
|
||||
dfu_data.flash_page_size = flash_page_size;
|
||||
dfu_data.flash_upload_size = flash_upload_size;
|
||||
|
||||
/* Initialize the USB driver with the right configuration */
|
||||
ret = usb_set_config(&dfu_config);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_DBG("Failed to config USB");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable USB driver */
|
||||
ret = usb_enable(&dfu_config);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_DBG("Failed to enable USB");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
/***************************************************************************
|
||||
*
|
||||
* Copyright(c) 2015,2016 Intel Corporation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief USB DFU device class driver header file
|
||||
*
|
||||
* Header file for USB DFU device class driver
|
||||
*/
|
||||
|
||||
#ifndef __USB_DFU_H__
|
||||
#define __USB_DFU_H__
|
||||
|
||||
#include <device.h>
|
||||
#include <usb/usb_common.h>
|
||||
|
||||
#define DFU_MAX_XFER_SIZE 256
|
||||
|
||||
#define DFU_NUM_CONF 0x01 /* Number of configurations for the USB Device */
|
||||
#define DFU_NUM_ITF 0x01 /* Number of interfaces in the configuration */
|
||||
#define DFU_NUM_EP 0x00 /* Number of Endpoints in the interface */
|
||||
|
||||
/* Class specific request */
|
||||
#define DFU_DETACH 0x00
|
||||
#define DFU_DNLOAD 0x01
|
||||
#define DFU_UPLOAD 0x02
|
||||
#define DFU_GETSTATUS 0x03
|
||||
#define DFU_CLRSTATUS 0x04
|
||||
#define DFU_GETSTATE 0x05
|
||||
#define DFU_ABORT 0x06
|
||||
|
||||
|
||||
/* DFU attributes */
|
||||
#define DFU_ATTR_CAN_DNLOAD 0x01
|
||||
#define DFU_ATTR_CAN_UPLOAD 0x02
|
||||
#define DFU_ATTR_MANIFESTATION_TOLERANT 0x4
|
||||
|
||||
#define DFU_DETACH_TIMEOUT 1000
|
||||
|
||||
/***** STATUS CODE ****/
|
||||
enum dfu_status {
|
||||
statusOK,
|
||||
errTARGET,
|
||||
errFILE,
|
||||
errWRITE,
|
||||
errERASE,
|
||||
errCHECK_ERASED,
|
||||
errPROG,
|
||||
errVERIFY,
|
||||
errADDRESS,
|
||||
errNOTDONE,
|
||||
errFIRMWARE,
|
||||
errVENDOR,
|
||||
errUSB,
|
||||
errPOR,
|
||||
errUNKNOWN,
|
||||
errSTALLEDPKT
|
||||
};
|
||||
|
||||
/**** STATES ****/
|
||||
enum dfu_state {
|
||||
appIDLE,
|
||||
appDETACH,
|
||||
dfuIDLE,
|
||||
dfuDNLOAD_SYNC,
|
||||
dfuDNBUSY,
|
||||
dfuDNLOAD_IDLE,
|
||||
dfuMANIFEST_SYNC,
|
||||
dfuMANIFEST,
|
||||
dfuMANIFEST_WAIT_RST,
|
||||
dfuUPLOAD_IDLE,
|
||||
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*ALT_SETTINGS + DFU)
|
||||
*/
|
||||
#define DFU_MODE_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \
|
||||
USB_INTERFACE_DESC_SIZE * DFU_MODE_ALTERNATE_SETTINGS + \
|
||||
USB_DFU_DESC_SIZE)
|
||||
|
||||
#define DFU_RUNTIME_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \
|
||||
USB_INTERFACE_DESC_SIZE * DFU_RUNTIME_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
|
||||
*
|
||||
* @param flash_dev Flash device driver to be used.
|
||||
* @param flash_base_addr Flash address to write/read to/from.
|
||||
* @param flash_page_size Flash page size.
|
||||
* @param flash_upload_size Flash upload data size.
|
||||
*
|
||||
* @return N/A.
|
||||
*/
|
||||
int dfu_start(struct device *flash_dev, u32_t flash_base_addr,
|
||||
u32_t flash_page_size, u32_t flash_upload_size);
|
||||
|
||||
#endif /* __USB_DFU_H__ */
|
|
@ -3,6 +3,7 @@ zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/usb)
|
|||
zephyr_sources_ifdef(CONFIG_USB_CDC_ACM cdc_acm.c)
|
||||
zephyr_sources_ifdef(CONFIG_USB_MASS_STORAGE mass_storage.c)
|
||||
zephyr_sources_ifdef(CONFIG_USB_DEVICE_BLUETOOTH bluetooth.c)
|
||||
zephyr_sources_ifdef(CONFIG_USB_DFU_CLASS usb_dfu.c)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_NETWORK netusb)
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_HID hid)
|
||||
|
|
|
@ -185,4 +185,23 @@ source "subsys/usb/class/netusb/Kconfig"
|
|||
|
||||
source "subsys/usb/class/hid/Kconfig"
|
||||
|
||||
config USB_DFU_CLASS
|
||||
bool
|
||||
prompt "USB DFU Class Driver"
|
||||
depends on IMG_MANAGER
|
||||
select MPU_ALLOW_FLASH_WRITE
|
||||
default n
|
||||
help
|
||||
USB DFU class driver
|
||||
|
||||
config USB_DFU_MAX_XFER_SIZE
|
||||
int
|
||||
depends on USB_DFU_CLASS
|
||||
default 64
|
||||
|
||||
config USB_DFU_DETACH_TIMEOUT
|
||||
int
|
||||
depends on USB_DFU_CLASS
|
||||
default 1000
|
||||
|
||||
endif # CONFIG_USB_DEVICE_STACK
|
||||
|
|
680
subsys/usb/class/usb_dfu.c
Normal file
680
subsys/usb/class/usb_dfu.c
Normal file
|
@ -0,0 +1,680 @@
|
|||
/*******************************************************************************
|
||||
*
|
||||
* Copyright(c) 2015,2016 Intel Corporation.
|
||||
* Copyright(c) 2017 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief DFU class driver
|
||||
*
|
||||
* USB DFU device class driver
|
||||
*
|
||||
*/
|
||||
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <flash.h>
|
||||
#include <dfu/mcuboot.h>
|
||||
#include <dfu/flash_img.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <usb/usb_device.h>
|
||||
#include <usb/usb_common.h>
|
||||
#include <usb/class/usb_dfu.h>
|
||||
#include "../usb_descriptor.h"
|
||||
#include "../composite.h"
|
||||
|
||||
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_USB_DEVICE_LEVEL
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
#define NUMOF_ALTERNATE_SETTINGS 2
|
||||
|
||||
#define IMAGE_0_DESC_LENGTH (sizeof(FLASH_AREA_IMAGE_0_LABEL) * 2)
|
||||
#define IMAGE_0_UC_IDX_MAX (IMAGE_0_DESC_LENGTH - 3)
|
||||
#define IMAGE_0_STRING_IDX_MAX (sizeof(FLASH_AREA_IMAGE_0_LABEL) - 2)
|
||||
|
||||
#define IMAGE_1_DESC_LENGTH (sizeof(FLASH_AREA_IMAGE_1_LABEL) * 2)
|
||||
#define IMAGE_1_UC_IDX_MAX (IMAGE_1_DESC_LENGTH - 3)
|
||||
#define IMAGE_1_STRING_IDX_MAX (sizeof(FLASH_AREA_IMAGE_1_LABEL) - 2)
|
||||
|
||||
#ifdef CONFIG_USB_COMPOSITE_DEVICE
|
||||
#define USB_DFU_MAX_XFER_SIZE CONFIG_USB_COMPOSITE_BUFFER_SIZE
|
||||
#else
|
||||
#define USB_DFU_MAX_XFER_SIZE CONFIG_USB_DFU_MAX_XFER_SIZE
|
||||
#endif
|
||||
|
||||
struct dev_dfu_mode_descriptor {
|
||||
struct usb_device_descriptor device_descriptor;
|
||||
struct usb_cfg_descriptor cfg_descr;
|
||||
struct usb_dfu_config {
|
||||
struct usb_if_descriptor if0;
|
||||
struct usb_if_descriptor if1;
|
||||
struct dfu_runtime_descriptor dfu_descr;
|
||||
} __packed dfu_cfg;
|
||||
struct usb_string_desription {
|
||||
struct usb_string_descriptor lang_descr;
|
||||
struct usb_mfr_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bString[MFR_DESC_LENGTH - 2];
|
||||
} __packed utf16le_mfr;
|
||||
|
||||
struct usb_product_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bString[PRODUCT_DESC_LENGTH - 2];
|
||||
} __packed utf16le_product;
|
||||
|
||||
struct usb_sn_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bString[SN_DESC_LENGTH - 2];
|
||||
} __packed utf16le_sn;
|
||||
|
||||
struct image_0_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bString[IMAGE_0_DESC_LENGTH - 2];
|
||||
} __packed utf16le_image0;
|
||||
|
||||
struct image_1_descriptor {
|
||||
u8_t bLength;
|
||||
u8_t bDescriptorType;
|
||||
u8_t bString[IMAGE_1_DESC_LENGTH - 2];
|
||||
} __packed utf16le_image1;
|
||||
} __packed string_descr;
|
||||
|
||||
struct usb_desc_header term_descr;
|
||||
} __packed;
|
||||
|
||||
static struct dev_dfu_mode_descriptor dfu_mode_desc = {
|
||||
/* Device descriptor */
|
||||
.device_descriptor = {
|
||||
.bLength = sizeof(struct usb_device_descriptor),
|
||||
.bDescriptorType = USB_DEVICE_DESC,
|
||||
.bcdUSB = sys_cpu_to_le16(USB_1_1),
|
||||
.bDeviceClass = 0,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
.bMaxPacketSize0 = MAX_PACKET_SIZE0,
|
||||
.idVendor = sys_cpu_to_le16((u16_t)CONFIG_USB_DEVICE_VID),
|
||||
.idProduct = sys_cpu_to_le16((u16_t)CONFIG_USB_DEVICE_PID),
|
||||
.bcdDevice = sys_cpu_to_le16(BCDDEVICE_RELNUM),
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1,
|
||||
},
|
||||
/* Configuration descriptor */
|
||||
.cfg_descr = {
|
||||
.bLength = sizeof(struct usb_cfg_descriptor),
|
||||
.bDescriptorType = USB_CONFIGURATION_DESC,
|
||||
.wTotalLength = sizeof(struct dev_dfu_mode_descriptor)
|
||||
- sizeof(struct usb_device_descriptor)
|
||||
- sizeof(struct usb_string_desription)
|
||||
- sizeof(struct usb_desc_header),
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = USB_CONFIGURATION_ATTRIBUTES,
|
||||
.bMaxPower = MAX_LOW_POWER,
|
||||
},
|
||||
.dfu_cfg = {
|
||||
/* Interface descriptor */
|
||||
.if0 = {
|
||||
.bLength = sizeof(struct usb_if_descriptor),
|
||||
.bDescriptorType = USB_INTERFACE_DESC,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = DFU_DEVICE_CLASS,
|
||||
.bInterfaceSubClass = DFU_SUBCLASS,
|
||||
.bInterfaceProtocol = DFU_MODE_PROTOCOL,
|
||||
.iInterface = 4,
|
||||
},
|
||||
.if1 = {
|
||||
.bLength = sizeof(struct usb_if_descriptor),
|
||||
.bDescriptorType = USB_INTERFACE_DESC,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = DFU_DEVICE_CLASS,
|
||||
.bInterfaceSubClass = DFU_SUBCLASS,
|
||||
.bInterfaceProtocol = DFU_MODE_PROTOCOL,
|
||||
.iInterface = 5,
|
||||
},
|
||||
.dfu_descr = {
|
||||
.bLength = sizeof(struct dfu_runtime_descriptor),
|
||||
.bDescriptorType = DFU_FUNC_DESC,
|
||||
.bmAttributes = DFU_ATTR_CAN_DNLOAD |
|
||||
DFU_ATTR_CAN_UPLOAD |
|
||||
DFU_ATTR_MANIFESTATION_TOLERANT,
|
||||
.wDetachTimeOut =
|
||||
sys_cpu_to_le16(CONFIG_USB_DFU_DETACH_TIMEOUT),
|
||||
.wTransferSize =
|
||||
sys_cpu_to_le16(USB_DFU_MAX_XFER_SIZE),
|
||||
.bcdDFUVersion =
|
||||
sys_cpu_to_le16(DFU_VERSION),
|
||||
},
|
||||
},
|
||||
.string_descr = {
|
||||
.lang_descr = {
|
||||
.bLength = sizeof(struct usb_string_descriptor),
|
||||
.bDescriptorType = USB_STRING_DESC,
|
||||
.bString = sys_cpu_to_le16(0x0409),
|
||||
},
|
||||
/* Manufacturer String Descriptor */
|
||||
.utf16le_mfr = {
|
||||
.bLength = MFR_DESC_LENGTH,
|
||||
.bDescriptorType = USB_STRING_DESC,
|
||||
.bString = CONFIG_USB_DEVICE_MANUFACTURER,
|
||||
},
|
||||
/* Product String Descriptor */
|
||||
.utf16le_product = {
|
||||
.bLength = PRODUCT_DESC_LENGTH,
|
||||
.bDescriptorType = USB_STRING_DESC,
|
||||
.bString = CONFIG_USB_DEVICE_PRODUCT,
|
||||
},
|
||||
/* Serial Number String Descriptor */
|
||||
.utf16le_sn = {
|
||||
.bLength = SN_DESC_LENGTH,
|
||||
.bDescriptorType = USB_STRING_DESC,
|
||||
.bString = CONFIG_USB_DEVICE_SN,
|
||||
},
|
||||
/* Image 0 String Descriptor */
|
||||
.utf16le_image0 = {
|
||||
.bLength = IMAGE_0_DESC_LENGTH,
|
||||
.bDescriptorType = USB_STRING_DESC,
|
||||
.bString = FLASH_AREA_IMAGE_0_LABEL,
|
||||
},
|
||||
/* Image 1 String Descriptor */
|
||||
.utf16le_image1 = {
|
||||
.bLength = IMAGE_1_DESC_LENGTH,
|
||||
.bDescriptorType = USB_STRING_DESC,
|
||||
.bString = FLASH_AREA_IMAGE_1_LABEL,
|
||||
},
|
||||
},
|
||||
.term_descr = {
|
||||
.bLength = 0,
|
||||
.bDescriptorType = 0,
|
||||
},
|
||||
};
|
||||
static struct usb_cfg_data dfu_config;
|
||||
|
||||
/* Device data structure */
|
||||
struct dfu_data_t {
|
||||
/* Flash device to read/write data from/to */
|
||||
struct device *flash_dev;
|
||||
/* Flash layout data (image-0, image-1, image-scratch) */
|
||||
u32_t flash_addr;
|
||||
u32_t flash_upload_size;
|
||||
/* Number of bytes sent during upload */
|
||||
u32_t bytes_sent;
|
||||
u32_t alt_setting; /* DFU alternate setting */
|
||||
#ifdef CONFIG_USB_COMPOSITE_DEVICE
|
||||
u8_t *buffer;
|
||||
#else
|
||||
u8_t buffer[USB_DFU_MAX_XFER_SIZE]; /* DFU data buffer */
|
||||
#endif
|
||||
struct flash_img_context ctx;
|
||||
enum dfu_state state; /* State of the DFU device */
|
||||
enum dfu_status status; /* Status of the DFU device */
|
||||
u16_t block_nr; /* DFU block number */
|
||||
};
|
||||
|
||||
static struct dfu_data_t dfu_data = {
|
||||
.state = appIDLE,
|
||||
.status = statusOK,
|
||||
.flash_addr = CONFIG_FLASH_BASE_ADDRESS + FLASH_AREA_IMAGE_1_OFFSET,
|
||||
.flash_upload_size = FLASH_AREA_IMAGE_1_SIZE,
|
||||
.alt_setting = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper function to check if in DFU app state.
|
||||
*
|
||||
* @return true if app state, false otherwise.
|
||||
*/
|
||||
static bool dfu_check_app_state(void)
|
||||
{
|
||||
if (dfu_data.state == appIDLE ||
|
||||
dfu_data.state == appDETACH) {
|
||||
dfu_data.state = appIDLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to reset DFU internal counters.
|
||||
*/
|
||||
static void dfu_reset_counters(void)
|
||||
{
|
||||
dfu_data.bytes_sent = 0;
|
||||
dfu_data.block_nr = 0;
|
||||
flash_img_init(&dfu_data.ctx, dfu_data.flash_dev);
|
||||
}
|
||||
|
||||
static void dfu_flash_write(u8_t *data, size_t len)
|
||||
{
|
||||
bool flush = false;
|
||||
|
||||
if (!len) {
|
||||
/* Download completed */
|
||||
flush = true;
|
||||
}
|
||||
|
||||
if (flash_img_buffered_write(&dfu_data.ctx, data, len, flush)) {
|
||||
SYS_LOG_ERR("flash write error");
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errWRITE;
|
||||
} else if (!len) {
|
||||
SYS_LOG_DBG("flash write done");
|
||||
dfu_data.state = dfuMANIFEST_SYNC;
|
||||
dfu_reset_counters();
|
||||
if (boot_request_upgrade(false)) {
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errWRITE;
|
||||
}
|
||||
} else {
|
||||
dfu_data.state = dfuDNLOAD_IDLE;
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("bytes written 0x%x",
|
||||
flash_img_bytes_written(&dfu_data.ctx));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handler called for DFU Class requests not handled by the USB stack.
|
||||
*
|
||||
* @param pSetup Information about the request to execute.
|
||||
* @param len Size of the buffer.
|
||||
* @param data Buffer containing the request result.
|
||||
*
|
||||
* @return 0 on success, negative errno code on fail.
|
||||
*/
|
||||
static int dfu_class_handle_req(struct usb_setup_packet *pSetup,
|
||||
s32_t *data_len, u8_t **data)
|
||||
{
|
||||
int ret;
|
||||
u32_t len, bytes_left;
|
||||
|
||||
switch (pSetup->bRequest) {
|
||||
case DFU_GETSTATUS:
|
||||
SYS_LOG_DBG("DFU_GETSTATUS: status %d, state %d",
|
||||
dfu_data.status, dfu_data.state);
|
||||
|
||||
if (dfu_data.state == dfuMANIFEST_SYNC) {
|
||||
dfu_data.state = dfuIDLE;
|
||||
}
|
||||
|
||||
(*data)[0] = dfu_data.status;
|
||||
(*data)[1] = 0;
|
||||
(*data)[2] = 1;
|
||||
(*data)[3] = 0;
|
||||
(*data)[4] = dfu_data.state;
|
||||
(*data)[5] = 0;
|
||||
*data_len = 6;
|
||||
break;
|
||||
|
||||
case DFU_GETSTATE:
|
||||
SYS_LOG_DBG("DFU_GETSTATE");
|
||||
(*data)[0] = dfu_data.state;
|
||||
*data_len = 1;
|
||||
break;
|
||||
|
||||
case DFU_ABORT:
|
||||
SYS_LOG_DBG("DFU_ABORT");
|
||||
|
||||
if (dfu_check_app_state()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dfu_reset_counters();
|
||||
dfu_data.state = dfuIDLE;
|
||||
dfu_data.status = statusOK;
|
||||
break;
|
||||
|
||||
case DFU_CLRSTATUS:
|
||||
SYS_LOG_DBG("DFU_CLRSTATUS");
|
||||
|
||||
if (dfu_check_app_state()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dfu_data.state = dfuIDLE;
|
||||
dfu_data.status = statusOK;
|
||||
break;
|
||||
|
||||
case DFU_DNLOAD:
|
||||
SYS_LOG_DBG("DFU_DNLOAD block %d, len %d, state %d",
|
||||
pSetup->wValue, pSetup->wLength, dfu_data.state);
|
||||
|
||||
if (dfu_check_app_state()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (dfu_data.state) {
|
||||
case dfuIDLE:
|
||||
SYS_LOG_DBG("DFU_DNLOAD start");
|
||||
dfu_reset_counters();
|
||||
if (dfu_data.flash_addr != FLASH_AREA_IMAGE_1_OFFSET) {
|
||||
dfu_data.status = errWRITE;
|
||||
dfu_data.state = dfuERROR;
|
||||
SYS_LOG_ERR("This area can not be overwritten");
|
||||
break;
|
||||
}
|
||||
|
||||
if (boot_erase_img_bank(FLASH_AREA_IMAGE_1_OFFSET)) {
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errERASE;
|
||||
break;
|
||||
}
|
||||
case dfuDNLOAD_IDLE:
|
||||
dfu_flash_write(*data, pSetup->wLength);
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_ERR("DFU_DNLOAD wrong state %d",
|
||||
dfu_data.state);
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errUNKNOWN;
|
||||
dfu_reset_counters();
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case DFU_UPLOAD:
|
||||
SYS_LOG_DBG("DFU_UPLOAD block %d, len %d, state %d",
|
||||
pSetup->wValue, pSetup->wLength, dfu_data.state);
|
||||
|
||||
if (dfu_check_app_state()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (dfu_data.state) {
|
||||
case dfuIDLE:
|
||||
dfu_reset_counters();
|
||||
SYS_LOG_DBG("DFU_UPLOAD start");
|
||||
case dfuUPLOAD_IDLE:
|
||||
if (!pSetup->wLength ||
|
||||
dfu_data.block_nr != pSetup->wValue) {
|
||||
SYS_LOG_DBG("DFU_UPLOAD block %d, expected %d, "
|
||||
"len %d", pSetup->wValue,
|
||||
dfu_data.block_nr, pSetup->wLength);
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errUNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Upload in progress */
|
||||
bytes_left = dfu_data.flash_upload_size -
|
||||
dfu_data.bytes_sent;
|
||||
if (bytes_left < pSetup->wLength) {
|
||||
len = bytes_left;
|
||||
} else {
|
||||
len = pSetup->wLength;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
ret = flash_read(dfu_data.flash_dev,
|
||||
dfu_data.flash_addr +
|
||||
dfu_data.bytes_sent,
|
||||
dfu_data.buffer, len);
|
||||
if (ret) {
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errFILE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*data_len = len;
|
||||
*data = dfu_data.buffer;
|
||||
|
||||
dfu_data.bytes_sent += len;
|
||||
dfu_data.block_nr++;
|
||||
|
||||
if (dfu_data.bytes_sent == dfu_data.flash_upload_size &&
|
||||
len < pSetup->wLength) {
|
||||
/* Upload completed when a
|
||||
* short packet is received
|
||||
*/
|
||||
*data_len = 0;
|
||||
dfu_data.state = dfuIDLE;
|
||||
} else
|
||||
dfu_data.state = dfuUPLOAD_IDLE;
|
||||
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_ERR("DFU_UPLOAD wrong state %d",
|
||||
dfu_data.state);
|
||||
dfu_data.state = dfuERROR;
|
||||
dfu_data.status = errUNKNOWN;
|
||||
dfu_reset_counters();
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case DFU_DETACH:
|
||||
SYS_LOG_DBG("DFU_DETACH timeout %d, state %d",
|
||||
pSetup->wValue, dfu_data.state);
|
||||
|
||||
if (dfu_data.state != appIDLE) {
|
||||
dfu_data.state = appIDLE;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Move to appDETACH state */
|
||||
dfu_data.state = appDETACH;
|
||||
|
||||
/* We should start a timer here but in order to
|
||||
* keep things simple and do not increase the size
|
||||
* we rely on the host to get us out of the appATTACHED
|
||||
* state if needed.
|
||||
*/
|
||||
|
||||
/* Set the DFU mode descriptors to be used after reset */
|
||||
dfu_config.usb_device_description = (u8_t *) &dfu_mode_desc;
|
||||
if (usb_set_config(&dfu_config) != 0) {
|
||||
SYS_LOG_ERR("usb_set_config failed in DFU_DETACH");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_WRN("DFU UNKNOWN STATE: %d", pSetup->bRequest);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback used to know the USB connection status
|
||||
*
|
||||
* @param status USB device status code.
|
||||
*
|
||||
* @return N/A.
|
||||
*/
|
||||
static void dfu_status_cb(enum usb_dc_status_code status, u8_t *param)
|
||||
{
|
||||
ARG_UNUSED(param);
|
||||
|
||||
/* Check the USB status and do needed action if required */
|
||||
switch (status) {
|
||||
case USB_DC_ERROR:
|
||||
SYS_LOG_DBG("USB device error");
|
||||
break;
|
||||
case USB_DC_RESET:
|
||||
SYS_LOG_DBG("USB device reset detected, state %d",
|
||||
dfu_data.state);
|
||||
if (dfu_data.state == appDETACH) {
|
||||
dfu_data.state = dfuIDLE;
|
||||
}
|
||||
break;
|
||||
case USB_DC_CONNECTED:
|
||||
SYS_LOG_DBG("USB device connected");
|
||||
break;
|
||||
case USB_DC_CONFIGURED:
|
||||
SYS_LOG_DBG("USB device configured");
|
||||
break;
|
||||
case USB_DC_DISCONNECTED:
|
||||
SYS_LOG_DBG("USB device disconnected");
|
||||
break;
|
||||
case USB_DC_SUSPEND:
|
||||
SYS_LOG_DBG("USB device supended");
|
||||
break;
|
||||
case USB_DC_RESUME:
|
||||
SYS_LOG_DBG("USB device resumed");
|
||||
break;
|
||||
case USB_DC_UNKNOWN:
|
||||
default:
|
||||
SYS_LOG_DBG("USB unknown state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
s32_t *data_len, u8_t **data)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
|
||||
if (REQTYPE_GET_RECIP(pSetup->bmRequestType) ==
|
||||
REQTYPE_RECIP_INTERFACE) {
|
||||
if (pSetup->bRequest == REQ_SET_INTERFACE) {
|
||||
SYS_LOG_DBG("DFU alternate setting %d", pSetup->wValue);
|
||||
|
||||
switch (pSetup->wValue) {
|
||||
case 0:
|
||||
dfu_data.flash_addr =
|
||||
CONFIG_FLASH_BASE_ADDRESS +
|
||||
FLASH_AREA_IMAGE_0_OFFSET;
|
||||
dfu_data.flash_upload_size =
|
||||
FLASH_AREA_IMAGE_0_SIZE;
|
||||
break;
|
||||
case 1:
|
||||
dfu_data.flash_addr =
|
||||
CONFIG_FLASH_BASE_ADDRESS +
|
||||
FLASH_AREA_IMAGE_1_OFFSET;
|
||||
dfu_data.flash_upload_size =
|
||||
FLASH_AREA_IMAGE_1_SIZE;
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_WRN("Invalid DFU alternate setting");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
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 = NULL,
|
||||
.cb_usb_status = dfu_status_cb,
|
||||
.interface = {
|
||||
.class_handler = dfu_class_handle_req,
|
||||
.custom_handler = dfu_custom_handle_req,
|
||||
.payload_data = NULL,
|
||||
},
|
||||
.num_endpoints = 0,
|
||||
};
|
||||
|
||||
static int usb_dfu_init(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
ascii7_to_utf16le(MFR_UC_IDX_MAX, MFR_STRING_IDX_MAX,
|
||||
(u8_t *)dfu_mode_desc.string_descr.utf16le_mfr.bString);
|
||||
|
||||
ascii7_to_utf16le(PRODUCT_UC_IDX_MAX, PRODUCT_STRING_IDX_MAX,
|
||||
(u8_t *)dfu_mode_desc.string_descr.utf16le_product.bString);
|
||||
|
||||
ascii7_to_utf16le(SN_UC_IDX_MAX, SN_STRING_IDX_MAX,
|
||||
(u8_t *)dfu_mode_desc.string_descr.utf16le_sn.bString);
|
||||
|
||||
ascii7_to_utf16le(IMAGE_0_UC_IDX_MAX, IMAGE_0_STRING_IDX_MAX,
|
||||
(u8_t *)dfu_mode_desc.string_descr.utf16le_image0.bString);
|
||||
|
||||
ascii7_to_utf16le(IMAGE_1_UC_IDX_MAX, IMAGE_1_STRING_IDX_MAX,
|
||||
(u8_t *)dfu_mode_desc.string_descr.utf16le_image1.bString);
|
||||
|
||||
dfu_data.flash_dev = device_get_binding(FLASH_DEV_NAME);
|
||||
if (!dfu_data.flash_dev) {
|
||||
SYS_LOG_ERR("Flash device not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_COMPOSITE_DEVICE
|
||||
ret = composite_add_function(&dfu_config, FIRST_IFACE_DFU);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_ERR("Failed to add a function");
|
||||
return ret;
|
||||
}
|
||||
dfu_data.buffer = dfu_config.interface.payload_data;
|
||||
#else
|
||||
dfu_config.interface.payload_data = dfu_data.buffer;
|
||||
dfu_config.usb_device_description = usb_get_device_descriptor();
|
||||
|
||||
/* Initialize the USB driver with the right configuration */
|
||||
ret = usb_set_config(&dfu_config);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_ERR("Failed to config USB");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable USB driver */
|
||||
ret = usb_enable(&dfu_config);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_ERR("Failed to enable USB");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(usb_dfu_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
|
@ -15,6 +15,7 @@
|
|||
#include <usb/class/usb_msc.h>
|
||||
#include <usb/class/usb_cdc.h>
|
||||
#include <usb/class/usb_hid.h>
|
||||
#include <usb/class/usb_dfu.h>
|
||||
#include "usb_descriptor.h"
|
||||
|
||||
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_USB_DEVICE_LEVEL
|
||||
|
@ -130,6 +131,12 @@ struct dev_common_descriptor {
|
|||
struct usb_ep_descriptor if0_out_ep;
|
||||
struct usb_ep_descriptor if0_in_ep;
|
||||
} __packed bluetooth_cfg;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_DFU_CLASS
|
||||
struct usb_dfu_config {
|
||||
struct usb_if_descriptor if0;
|
||||
struct dfu_runtime_descriptor dfu_descr;
|
||||
} __packed dfu_cfg;
|
||||
#endif
|
||||
struct usb_string_desription {
|
||||
struct usb_string_descriptor lang_descr;
|
||||
|
@ -724,6 +731,35 @@ static struct dev_common_descriptor common_desc = {
|
|||
},
|
||||
},
|
||||
#endif /* CONFIG_USB_DEVICE_BLUETOOTH */
|
||||
#ifdef CONFIG_USB_DFU_CLASS
|
||||
.dfu_cfg = {
|
||||
/* Interface descriptor */
|
||||
.if0 = {
|
||||
.bLength = sizeof(struct usb_if_descriptor),
|
||||
.bDescriptorType = USB_INTERFACE_DESC,
|
||||
.bInterfaceNumber = FIRST_IFACE_DFU,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = DFU_DEVICE_CLASS,
|
||||
.bInterfaceSubClass = DFU_SUBCLASS,
|
||||
.bInterfaceProtocol = DFU_RT_PROTOCOL,
|
||||
.iInterface = 0,
|
||||
},
|
||||
.dfu_descr = {
|
||||
.bLength = sizeof(struct dfu_runtime_descriptor),
|
||||
.bDescriptorType = DFU_FUNC_DESC,
|
||||
.bmAttributes = DFU_ATTR_CAN_DNLOAD |
|
||||
DFU_ATTR_CAN_UPLOAD |
|
||||
DFU_ATTR_MANIFESTATION_TOLERANT,
|
||||
.wDetachTimeOut =
|
||||
sys_cpu_to_le16(CONFIG_USB_DFU_DETACH_TIMEOUT),
|
||||
.wTransferSize =
|
||||
sys_cpu_to_le16(CONFIG_USB_DFU_MAX_XFER_SIZE),
|
||||
.bcdDFUVersion =
|
||||
sys_cpu_to_le16(DFU_VERSION),
|
||||
},
|
||||
},
|
||||
#endif /* CONFIG_USB_DFU_CLASS */
|
||||
.string_descr = {
|
||||
.lang_descr = {
|
||||
.bLength = sizeof(struct usb_string_descriptor),
|
||||
|
|
|
@ -70,10 +70,16 @@
|
|||
#define NUMOF_ENDPOINTS_BLUETOOTH 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_DFU_CLASS
|
||||
#define NUMOF_IFACES_DFU 1
|
||||
#else
|
||||
#define NUMOF_IFACES_DFU 0
|
||||
#endif
|
||||
|
||||
#define NUMOF_IFACES (NUMOF_IFACES_CDC_ACM + NUMOF_IFACES_MASS + \
|
||||
NUMOF_IFACES_RNDIS + NUMOF_IFACES_CDC_ECM + \
|
||||
NUMOF_IFACES_HID + NUMOF_IFACES_CDC_EEM + \
|
||||
NUMOF_IFACES_BLUETOOTH)
|
||||
NUMOF_IFACES_BLUETOOTH + NUMOF_IFACES_DFU)
|
||||
#define NUMOF_ENDPOINTS (NUMOF_ENDPOINTS_CDC_ACM + NUMOF_ENDPOINTS_MASS + \
|
||||
NUMOF_ENDPOINTS_RNDIS + NUMOF_ENDPOINTS_CDC_ECM + \
|
||||
NUMOF_ENDPOINTS_HID + NUMOF_ENDPOINTS_CDC_EEM + \
|
||||
|
@ -92,6 +98,8 @@
|
|||
NUMOF_IFACES_HID)
|
||||
#define FIRST_IFACE_BLUETOOTH (FIRST_IFACE_CDC_EEM + \
|
||||
NUMOF_IFACES_CDC_EEM)
|
||||
#define FIRST_IFACE_DFU (FIRST_IFACE_BLUETOOTH + \
|
||||
NUMOF_IFACES_BLUETOOTH)
|
||||
|
||||
#define MFR_DESC_LENGTH (sizeof(CONFIG_USB_DEVICE_MANUFACTURER) * 2)
|
||||
#define MFR_UC_IDX_MAX (MFR_DESC_LENGTH - 3)
|
||||
|
|
Loading…
Reference in a new issue