/** | |
* \file | |
* | |
* \brief USB Device Firmware Upgrade (DFU) interface definitions. | |
* | |
* Copyright (C) 2009 Atmel Corporation. All rights reserved. | |
* | |
* \page License | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* | |
* 2. 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. | |
* | |
* 3. The name of Atmel may not be used to endorse or promote products derived | |
* from this software without specific prior written permission. | |
* | |
* 4. This software may only be redistributed and used in connection with an | |
* Atmel AVR product. | |
* | |
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED | |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. | |
*/ | |
#include "conf_usb.h" | |
#include "usb_protocol.h" | |
#include "usb_protocol_dfu.h" | |
#include "usb_atmel_dfu.h" | |
#include "udd.h" | |
#include "udc.h" | |
#include "udi_dfu_atmel.h" | |
#include "isp.h" | |
#include "string.h" | |
#ifndef UDI_DFU_ATMEL_PROTOCOL_VERSION | |
# error UDI_DFU_ATMEL_PROTOCOL_VERSION must be define in conf_usb.h | |
#endif | |
#if ((UDI_DFU_ATMEL_PROTOCOL_VERSION != DFU_ATMEL_PROTOCOL_VERSION_2) \ | |
&& (UDI_DFU_ATMEL_PROTOCOL_VERSION != DFU_ATMEL_PROTOCOL_VERSION_1)) | |
# error Bootloader protocol not supported (UDI_DFU_ATMEL_PROTOCOL_VERSION) | |
#endif | |
/** | |
* \addtogroup udi_dfu_atmel_group | |
* @{ | |
*/ | |
/** | |
* \name Interface for UDC | |
*/ | |
//@{ | |
bool udi_dfu_atmel_enable(void); | |
void udi_dfu_atmel_disable(void); | |
bool udi_dfu_atmel_setup(void); | |
uint8_t udi_dfu_atmel_getsetting(void); | |
//! Global structure which contains standard UDI API for UDC | |
UDC_DESC_STORAGE udi_api_t udi_api_dfu_atmel = { | |
.enable = udi_dfu_atmel_enable, | |
.disable = udi_dfu_atmel_disable, | |
.setup = udi_dfu_atmel_setup, | |
.getsetting = udi_dfu_atmel_getsetting, | |
}; | |
//@} | |
/** | |
* \name Internal routines to manage DFU requests | |
*/ | |
//@{ | |
static void udi_dfu_atmel_reset_protocol(void); | |
static void udi_dfu_atmel_reset_cpu(void); | |
static bool udi_dfu_atmel_cmd_decode(void); | |
static void udi_dfu_atmel_sel_mem( uint8_t mem_num ); | |
static bool udi_dfu_atmel_mem_protected(void); | |
static bool udi_dfu_atmel_mem_getaddr(uint8_t * arg); | |
static bool udi_dfu_atmel_mem_read(void); | |
static void udi_dfu_atmel_mem_check(void); | |
static bool udi_dfu_atmel_mem_send_last_add(void); | |
static bool udi_dfu_atmel_mem_write(void); | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
static bool udi_dfu_atmel_program(void); | |
static bool udi_dfu_atmel_read(void); | |
static bool udi_dfu_atmel_blankcheck(void); | |
static bool udi_dfu_atmel_erase_chip(void); | |
static void udi_dfu_atmel_start(void); | |
static bool udi_dfu_atmel_select_memory(void); | |
#else // V1 | |
static bool udi_dfu_atmel_progstart(uint8_t mem); | |
static bool udi_dfu_atmel_read(uint8_t mem, bool b_check); | |
static void udi_dfu_atmel_read_id(uint8_t mem, uint8_t addr); | |
static bool udi_dfu_atmel_chip_erase(void); | |
static void udi_dfu_atmel_start_app(uint8_t mode); | |
#ifndef ISP_SMALL_MEMORY_SIZE | |
static bool udi_dfu_atmel_cmd_decode_changeaddr(void); | |
#endif | |
#endif | |
//@} | |
#define CAT_CMD(val1,val2) (((uint16_t)val1<<8)|(val2<<0)) | |
/** | |
* \name Internal variables to manage DFU requests | |
*/ | |
//@{ | |
static dfu_status_t udi_dfu_atmel_status; | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
# ifdef UDI_DFU_ATMEL_PROTOCOL_2_SPLIT_ERASE_CHIP | |
static bool udi_dfu_atmel_erase_running; | |
# endif | |
#endif | |
//! Structure to store the command fields | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
static dfu_atmel_v2_cmd_t udi_dfu_atmel_cmd; | |
#else // V1 | |
static dfu_atmel_v1_cmd_t udi_dfu_atmel_cmd; | |
#endif | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
#define DFU_ATMEL_BUF_TRANS_SIZE DFU_ATMEL_V2_BUF_TRANS_SIZE | |
#else // V1 | |
#define DFU_ATMEL_BUF_TRANS_SIZE DFU_ATMEL_V1_BUF_TRANS_SIZE | |
#endif | |
//! Buffer to receive or send data | |
COMPILER_WORD_ALIGNED | |
static uint8_t | |
udi_dfu_atmel_buf_trans[DFU_ATMEL_BUF_TRANS_SIZE]; | |
//! Callback to use when an upload request is received | |
static bool(*udi_dfu_atmel_upload_callback) (void); | |
//! Notify a reset request to start | |
static void (*udi_dfu_atmel_reset_callback) (void); | |
//! Store the current security level | |
static bool udi_dfu_atmel_security; | |
/** | |
* \name To manage memories | |
*/ | |
//@{ | |
/** | |
* \name To manage memory transfers | |
*/ | |
//@{ | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
static bool udi_dfu_atmel_mem_b_protected; | |
#endif | |
static isp_addr_t udi_dfu_atmel_mem_add; | |
static isp_addr_t udi_dfu_atmel_mem_nb_data; | |
static isp_mem_t udi_dfu_atmel_mem_sel; | |
//@} | |
//@} | |
//@} | |
bool udi_dfu_atmel_enable(void) | |
{ | |
udi_dfu_atmel_reset_protocol(); | |
// Load chip information | |
isp_init(); | |
udi_dfu_atmel_security = isp_is_security(); | |
return UDI_DFU_ENABLE_EXT(); | |
} | |
void udi_dfu_atmel_disable(void) | |
{ | |
UDI_DFU_DISABLE_EXT(); | |
} | |
bool udi_dfu_atmel_setup(void) | |
{ | |
//** Interface requests | |
if (Udd_setup_type() != USB_REQ_TYPE_CLASS) { | |
return false; // Only class request decoded | |
} | |
if (Udd_setup_is_in()) { | |
// Requests Class Interface Get | |
switch (udd_g_ctrlreq.req.bRequest) { | |
case USB_REQ_DFU_GETSTATUS: | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
# ifdef UDI_DFU_ATMEL_PROTOCOL_2_SPLIT_ERASE_CHIP | |
if (udi_dfu_atmel_erase_running) { | |
udi_dfu_atmel_erase_chip(); | |
} | |
# endif | |
#endif | |
Assert(udd_g_ctrlreq.req.wValue==0); | |
Assert(sizeof(udi_dfu_atmel_status)==udd_g_ctrlreq.req.wLength); | |
udd_set_setup_payload( | |
(uint8_t *) & udi_dfu_atmel_status, | |
sizeof(udi_dfu_atmel_status)); | |
return true; | |
// Used to send data to the host | |
// when the previous Atmel command (DNLOAD) request data | |
case USB_REQ_DFU_UPLOAD: | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
# ifdef UDI_DFU_ATMEL_PROTOCOL_2_SPLIT_ERASE_CHIP | |
Assert( !udi_dfu_atmel_erase_running ); | |
# endif | |
#endif | |
Assert(DFU_STATE_DFUERROR != udi_dfu_atmel_status.bState); | |
if (NULL != udi_dfu_atmel_upload_callback) { | |
return udi_dfu_atmel_upload_callback(); | |
} | |
} | |
} | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
# ifdef UDI_DFU_ATMEL_PROTOCOL_2_SPLIT_ERASE_CHIP | |
Assert( !udi_dfu_atmel_erase_running ); | |
# endif | |
#endif | |
if (Udd_setup_is_out()) { | |
// Requests Class Interface Set | |
switch (udd_g_ctrlreq.req.bRequest) { | |
case USB_REQ_DFU_CLRSTATUS: | |
Assert(udd_g_ctrlreq.req.wValue==0); | |
Assert(udd_g_ctrlreq.req.wLength==0); | |
udi_dfu_atmel_reset_protocol(); | |
return true; | |
// DNLOAD request including Atmel command fields | |
// and data for write operation. | |
// For read operation, the data are sent in the next UPLOAD request | |
case USB_REQ_DFU_DNLOAD: | |
// Check if a reset has been requested | |
if (NULL != udi_dfu_atmel_reset_callback) { | |
// Remove force ISP before a reset CPU to start Application | |
isp_force_isp(false); | |
// Then this DNLOAD DFU request must be empty (0==wLength) | |
// and valid the reset application command. | |
// Valid SETUP request and reset application via request callback. | |
udd_g_ctrlreq.callback = udi_dfu_atmel_reset_cpu; | |
return true; | |
} | |
Assert(DFU_STATE_DFUERROR != udi_dfu_atmel_status.bState); | |
Assert(udd_g_ctrlreq.req.wLength!=0); | |
// The first packet contains the command | |
// after this packet the over_under_run callback can be called | |
// if the Host want to send more data to device | |
udd_set_setup_payload( | |
(uint8_t *) & udi_dfu_atmel_cmd, | |
sizeof(udi_dfu_atmel_cmd)), | |
// Called when the first packet is received | |
// before continuing DATA phase or start ZLP phase | |
udd_g_ctrlreq.over_under_run = udi_dfu_atmel_cmd_decode; | |
// Note udd_g_ctrlreq.callback is updated | |
// by udi_dfu_atmel_cmd_decode() before ZLP phase | |
return true; | |
} | |
} | |
// Unknown request | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRSTALLEDPK; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; | |
} | |
uint8_t udi_dfu_atmel_getsetting(void) | |
{ | |
return 0; | |
} | |
static void udi_dfu_atmel_reset_protocol(void) | |
{ | |
// Reset DFU status | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_OK; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUIDLE; | |
// These fields are not used and always set to zero: | |
// bwPollTimeout[3] | |
// iString | |
// Reset all callbacks | |
udd_g_ctrlreq.over_under_run = NULL; | |
udd_g_ctrlreq.callback = NULL; | |
udi_dfu_atmel_reset_callback = NULL; | |
} | |
static void udi_dfu_atmel_reset_cpu(void) | |
{ | |
udi_dfu_atmel_reset_callback(); | |
} | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
//! Called by over_under_run when the first data packet | |
//! from a DNLOAD DFU request is received | |
static bool udi_dfu_atmel_cmd_decode(void) | |
{ | |
// By default no callback initialized | |
// By default request states are success and finish | |
udi_dfu_atmel_reset_protocol(); | |
udi_dfu_atmel_upload_callback = NULL; | |
// To restart ISP in case of USB cable is unplug during program load | |
isp_force_isp(true); | |
// Decode Atmel command ID | |
switch (CAT_CMD(udi_dfu_atmel_cmd.group, udi_dfu_atmel_cmd.cmd_id)) { | |
case CAT_CMD(DFU_ATMEL_V2_CMD_GRP_DNLOAD, | |
DFU_ATMEL_V2_CMD_PROGRAM_START): | |
return udi_dfu_atmel_program(); | |
case CAT_CMD(DFU_ATMEL_V2_CMD_GRP_UPLOAD, | |
DFU_ATMEL_V2_CMD_READ_MEMORY): | |
return udi_dfu_atmel_read(); | |
case CAT_CMD(DFU_ATMEL_V2_CMD_GRP_UPLOAD, | |
DFU_ATMEL_V2_CMD_BLANK_CHECK): | |
return udi_dfu_atmel_blankcheck(); | |
case CAT_CMD(DFU_ATMEL_V2_CMD_GRP_EXEC, | |
DFU_ATMEL_V2_CMD_ERASE): | |
return udi_dfu_atmel_erase_chip(); | |
case CAT_CMD(DFU_ATMEL_V2_CMD_GRP_EXEC, | |
DFU_ATMEL_V2_CMD_START_APPLI): | |
udi_dfu_atmel_start(); | |
return true; | |
case CAT_CMD(DFU_ATMEL_V2_CMD_GRP_SELECT, | |
DFU_ATMEL_V2_CMD_SELECT_MEMORY): | |
return udi_dfu_atmel_select_memory(); | |
} | |
return false; // Unknow command | |
} | |
static bool udi_dfu_atmel_program(void) | |
{ | |
// Check list before start memory programmation | |
if (udi_dfu_atmel_security && udi_dfu_atmel_mem_b_protected) { | |
// Security enabled | |
return udi_dfu_atmel_mem_protected(); | |
} | |
if (!udi_dfu_atmel_mem_sel.fnct_write) { | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRWRITE; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; // Write memory not available | |
} | |
if (!udi_dfu_atmel_mem_getaddr(&udi_dfu_atmel_cmd.arg[0])) | |
return false; // Bad Range | |
// Init buffer to fill during next DATA phase of request | |
udd_set_setup_payload( | |
udi_dfu_atmel_buf_trans, | |
DFU_ATMEL_BUF_TRANS_SIZE); | |
// Init callback called after buffer filled | |
udd_g_ctrlreq.over_under_run = udi_dfu_atmel_mem_write; | |
return true; | |
} | |
static bool udi_dfu_atmel_read(void) | |
{ | |
// Check before decoding the command | |
if (!udi_dfu_atmel_mem_getaddr(&udi_dfu_atmel_cmd.arg[0])) | |
return false; // Range bad | |
if (!udi_dfu_atmel_mem_sel.fnct_read) { | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRWRITE; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; // Read memory not available | |
} | |
if (udi_dfu_atmel_security && udi_dfu_atmel_mem_b_protected) { | |
// Memory security then remove read callback by protected callback | |
udi_dfu_atmel_upload_callback = udi_dfu_atmel_mem_protected; | |
}else{ | |
udi_dfu_atmel_upload_callback = udi_dfu_atmel_mem_read; | |
} | |
return true; | |
} | |
static bool udi_dfu_atmel_blankcheck(void) | |
{ | |
// Check before decoding the command | |
if (!udi_dfu_atmel_mem_getaddr(&udi_dfu_atmel_cmd.arg[0])) | |
return false; // Range bad | |
if (!udi_dfu_atmel_mem_sel.fnct_read) { | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRWRITE; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; // Read memory not available | |
} | |
udi_dfu_atmel_mem_check(); | |
return true; | |
} | |
static bool udi_dfu_atmel_erase_chip(void) | |
{ | |
Assert(udi_dfu_atmel_cmd.arg[0]==DFU_ATMEL_V2_CMD_ERASE_ARG_CHIP); | |
#ifdef UDI_DFU_ATMEL_PROTOCOL_2_SPLIT_ERASE_CHIP | |
if (isp_erase_chip()) { | |
// Erase finish | |
udi_dfu_atmel_security = false; | |
udi_dfu_atmel_erase_running = false; | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_OK; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUIDLE; | |
}else{ | |
// Erase on-going | |
udi_dfu_atmel_erase_running = true; | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRNOTDONE; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUDNBUSY; | |
} | |
#else | |
if (!isp_erase_chip()) { | |
return false; | |
} | |
// Erase finish | |
udi_dfu_atmel_security = false; | |
#endif | |
return true; | |
} | |
static void udi_dfu_atmel_start(void) | |
{ | |
// Start application reset after next DNLOAD request | |
if (udi_dfu_atmel_cmd.arg[0] == DFU_ATMEL_V2_CMD_START_APPLI_ARG_RESET) { | |
udi_dfu_atmel_reset_callback = isp_start_appli_rst; | |
}else{ | |
Assert(app == DFU_ATMEL_V2_CMD_START_APPLI_ARG_NO_RESET); | |
udi_dfu_atmel_reset_callback = isp_start_appli_norst; | |
} | |
} | |
static bool udi_dfu_atmel_select_memory(void) | |
{ | |
switch (udi_dfu_atmel_cmd.arg[0]) { | |
case DFU_ATMEL_V2_CMD_SELECT_MEMORY_ARG_UNIT: | |
if (DFU_ATMEL_V2_MEM_COUNT <= udi_dfu_atmel_cmd.arg[1]) { | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRADDRESS; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; // Memory id error | |
} | |
udi_dfu_atmel_sel_mem(udi_dfu_atmel_cmd.arg[1]); | |
udi_dfu_atmel_mem_add = 0; | |
break; | |
#ifndef ISP_SMALL_MEMORY_SIZE | |
case DFU_ATMEL_V2_CMD_SELECT_MEMORY_ARG_PAGE: | |
{ | |
uint32_t tmp = 0; | |
MSB0W(tmp) = udi_dfu_atmel_cmd.arg[1]; | |
MSB1W(tmp) = udi_dfu_atmel_cmd.arg[2]; | |
if (tmp >= udi_dfu_atmel_mem_sel.size) { | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRADDRESS; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; // Address error | |
} | |
udi_dfu_atmel_mem_add = tmp; | |
} | |
break; | |
#endif | |
default: | |
Assert(false); // Bad command | |
break; | |
} | |
return true; | |
} | |
#else // V1 | |
// Called when a DNLOAD DFU request is received | |
// The DFU Atmel command are sending in DNLOAD request | |
// | |
// Note: An Atmel DFU commands can be stalled in following cases: | |
// memory security, bad address or blank check fail | |
static bool udi_dfu_atmel_cmd_decode(void) | |
{ | |
// By default no callback initialized | |
// By default request states are success and finish | |
udi_dfu_atmel_reset_protocol(); | |
udi_dfu_atmel_upload_callback = NULL; | |
// Decode Atmel command ID | |
switch (CAT_CMD(udi_dfu_atmel_cmd.cmd_id, | |
udi_dfu_atmel_cmd.arg[0])) { | |
#ifndef ISP_SMALL_MEMORY_SIZE | |
// Command to change high address | |
case CAT_CMD(DFU_ATMEL_V1_CMD_CHANGE_BASE_ADDR, | |
DFU_ATMEL_V1_CMD_CHANGE_BASE_ADDR_ARG0): | |
return udi_dfu_atmel_cmd_decode_changeaddr(); | |
#endif | |
// Commands to program a memory | |
case CAT_CMD(DFU_ATMEL_V1_CMD_PROG_START, | |
DFU_ATMEL_V1_CMD_PROG_START_ARG_FLASH): | |
return udi_dfu_atmel_progstart(DFU_ATMEL_V1_MEM_FLASH); | |
case CAT_CMD(DFU_ATMEL_V1_CMD_PROG_START, | |
DFU_ATMEL_V1_CMD_PROG_START_ARG_EEPROM): | |
return udi_dfu_atmel_progstart(DFU_ATMEL_V1_MEM_EEPROM); | |
case CAT_CMD(DFU_ATMEL_V1_CMD_PROG_START, | |
DFU_ATMEL_V1_CMD_PROG_START_ARG_CUSTOM): | |
return udi_dfu_atmel_progstart(DFU_ATMEL_V1_MEM_CUSTOM); | |
// Commands to read a memory | |
case CAT_CMD(DFU_ATMEL_V1_CMD_READ, | |
DFU_ATMEL_V1_CMD_READ_ARG_FLASH): | |
return udi_dfu_atmel_read(DFU_ATMEL_V1_MEM_FLASH,false); | |
case CAT_CMD(DFU_ATMEL_V1_CMD_READ, | |
DFU_ATMEL_V1_CMD_READ_ARG_EEPROM): | |
return udi_dfu_atmel_read(DFU_ATMEL_V1_MEM_EEPROM,false); | |
case CAT_CMD(DFU_ATMEL_V1_CMD_READ, | |
DFU_ATMEL_V1_CMD_READ_ARG_CUSTOM): | |
return udi_dfu_atmel_read(DFU_ATMEL_V1_MEM_CUSTOM,false); | |
// Commands to blank check a memory | |
case CAT_CMD(DFU_ATMEL_V1_CMD_READ, | |
DFU_ATMEL_V1_CMD_READ_ARG_FLASHCHECK): | |
return udi_dfu_atmel_read(DFU_ATMEL_V1_MEM_FLASH,true); | |
} | |
switch (CAT_CMD(udi_dfu_atmel_cmd.cmd_id, | |
udi_dfu_atmel_cmd.arg[0])) { | |
// Commands to erase chip | |
case CAT_CMD(DFU_ATMEL_V1_CMD_WRITE, | |
DFU_ATMEL_V1_CMD_WRITE_ARG_ERASE): | |
return udi_dfu_atmel_chip_erase(); | |
// Commands to start application | |
case CAT_CMD(DFU_ATMEL_V1_CMD_WRITE, | |
DFU_ATMEL_V1_CMD_WRITE_ARG_RST): | |
udi_dfu_atmel_start_app(udi_dfu_atmel_cmd.arg[1]); | |
return true; | |
// Commands to read Bootloader version | |
case CAT_CMD(DFU_ATMEL_V1_CMD_READ_ID, | |
DFU_ATMEL_V1_CMD_READ_ID_ARG_BOOTLOADER): | |
udi_dfu_atmel_read_id(DFU_ATMEL_V1_MEM_BOOTLOADER, | |
udi_dfu_atmel_cmd.arg[1]); | |
return true; | |
// Commands to read Chip indentification | |
case CAT_CMD(DFU_ATMEL_V1_CMD_READ_ID, | |
DFU_ATMEL_V1_CMD_READ_ID_ARG_SIGNATURE): | |
switch (udi_dfu_atmel_cmd.arg[1]) { | |
case DFU_ATMEL_V1_CMD_READ_ID_SIGNATURE_ARG_MANUF: | |
udi_dfu_atmel_read_id(DFU_ATMEL_V1_MEM_SIGNATURE,0); | |
break; | |
case DFU_ATMEL_V1_CMD_READ_ID_SIGNATURE_ARG_FAMILY: | |
udi_dfu_atmel_read_id(DFU_ATMEL_V1_MEM_SIGNATURE,1); | |
break; | |
case DFU_ATMEL_V1_CMD_READ_ID_SIGNATURE_ARG_PRODUCT: | |
udi_dfu_atmel_read_id(DFU_ATMEL_V1_MEM_SIGNATURE,2); | |
break; | |
case DFU_ATMEL_V1_CMD_READ_ID_SIGNATURE_ARG_REVISION: | |
udi_dfu_atmel_read_id(DFU_ATMEL_V1_MEM_SIGNATURE,3); | |
break; | |
} | |
return true; | |
} | |
return false; // Unknow command | |
} | |
static bool udi_dfu_atmel_progstart(uint8_t mem) | |
{ | |
udi_dfu_atmel_sel_mem(mem); | |
if (udi_dfu_atmel_security) { | |
return udi_dfu_atmel_mem_protected(); | |
} | |
if (!udi_dfu_atmel_mem_getaddr(&udi_dfu_atmel_cmd.arg[1])) { | |
return false; // Bad Range | |
} | |
// Init buffer to fill during next DATA phase of request | |
udd_set_setup_payload( | |
udi_dfu_atmel_buf_trans, | |
DFU_ATMEL_BUF_TRANS_SIZE); | |
// Init callback called after buffer filled | |
udd_g_ctrlreq.over_under_run = udi_dfu_atmel_mem_write; | |
return true; | |
} | |
static bool udi_dfu_atmel_read(uint8_t mem, bool b_check) | |
{ | |
udi_dfu_atmel_sel_mem( mem ); | |
if (!udi_dfu_atmel_mem_getaddr(&udi_dfu_atmel_cmd.arg[1])) | |
return false; // Bad Range | |
if ((!udi_dfu_atmel_mem_sel.fnct_read) || udi_dfu_atmel_security) { | |
// Read memory not available OR memory protected | |
// then accept request but stall next UPLOAD DFU request | |
udi_dfu_atmel_upload_callback = udi_dfu_atmel_mem_protected; | |
return true; | |
} | |
if (b_check) { | |
// It is not a read operation then it is a blanc check, thus do it now | |
udi_dfu_atmel_mem_check(); | |
}else{ | |
udi_dfu_atmel_upload_callback = udi_dfu_atmel_mem_read; | |
} | |
return true; | |
} | |
static bool udi_dfu_atmel_chip_erase(void) | |
{ | |
Assert(udi_dfu_atmel_cmd.arg[1]==DFU_ATMEL_V1_CMD_WRITE_ARG_ERASE_CHIP); | |
if (!isp_erase_chip()) { | |
return false; | |
} | |
udi_dfu_atmel_security = false; | |
return true; | |
} | |
static void udi_dfu_atmel_start_app(uint8_t mode) | |
{ | |
// Start application reset after next DNLOAD request | |
if (mode == DFU_ATMEL_V1_CMD_WRITE_ARG_RST_HW) { | |
udi_dfu_atmel_reset_callback = isp_start_appli_rst; | |
}else{ | |
Assert(app == DFU_ATMEL_V1_CMD_WRITE_ARG_RST_SF); | |
udi_dfu_atmel_reset_callback = isp_start_appli_norst; | |
} | |
} | |
static void udi_dfu_atmel_read_id(uint8_t mem, uint8_t addr) | |
{ | |
udi_dfu_atmel_sel_mem( mem ); | |
udi_dfu_atmel_mem_add = addr; | |
udi_dfu_atmel_mem_nb_data = 1; | |
udi_dfu_atmel_upload_callback = udi_dfu_atmel_mem_read; | |
} | |
#ifndef ISP_SMALL_MEMORY_SIZE | |
static bool udi_dfu_atmel_cmd_decode_changeaddr(void) | |
{ | |
udi_dfu_atmel_mem_add = ((uint32_t)udi_dfu_atmel_cmd.arg[2])<<16; | |
return true; | |
} | |
#endif | |
#endif // Protocol V1 or V2 | |
static void udi_dfu_atmel_sel_mem( uint8_t mem_num ) | |
{ | |
#if (UDI_DFU_ATMEL_PROTOCOL_VERSION == DFU_ATMEL_PROTOCOL_VERSION_2) | |
if((mem_num!=DFU_ATMEL_V2_MEM_CONFIGURATION) | |
&&(mem_num!=DFU_ATMEL_V2_MEM_SECURITY) | |
&&(mem_num!=DFU_ATMEL_V2_MEM_BOOTLOADER) | |
&&(mem_num!=DFU_ATMEL_V2_MEM_SIGNATURE)) { | |
udi_dfu_atmel_mem_b_protected = true; | |
}else{ | |
udi_dfu_atmel_mem_b_protected = false; | |
} | |
#endif | |
udi_dfu_atmel_mem_sel = *isp_memories.mem[mem_num]; | |
} | |
static bool udi_dfu_atmel_mem_protected(void) | |
{ | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRWRITE; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUIDLE; | |
return false; | |
} | |
static bool udi_dfu_atmel_mem_getaddr(uint8_t * arg) | |
{ | |
isp_addr_t addr_end; | |
// Get address for request argument | |
udi_dfu_atmel_mem_add = | |
(udi_dfu_atmel_mem_add&0xFFFF0000) + ((uint16_t)arg[0]<<8) + (arg[1]<<0); | |
addr_end = | |
(udi_dfu_atmel_mem_add&0xFFFF0000) + ((uint16_t)arg[2]<<8) + (arg[3]<<0); | |
Assert(addr_end >= udi_dfu_atmel_mem_add); | |
// Check address | |
if (addr_end >= udi_dfu_atmel_mem_sel.size) { | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRADDRESS; | |
udi_dfu_atmel_status.bState = DFU_STATE_DFUERROR; | |
return false; | |
} | |
// Compute the number of data to transfer | |
udi_dfu_atmel_mem_nb_data = addr_end - udi_dfu_atmel_mem_add + 1; | |
return true; | |
} | |
static bool udi_dfu_atmel_mem_read(void) | |
{ | |
Assert(udi_dfu_atmel_mem_nb_data <= DFU_ATMEL_BUF_TRANS_SIZE); | |
udi_dfu_atmel_mem_sel.fnct_read(udi_dfu_atmel_buf_trans, | |
udi_dfu_atmel_mem_add, udi_dfu_atmel_mem_nb_data); | |
// Init buffer to transfer | |
udd_set_setup_payload( udi_dfu_atmel_buf_trans, udi_dfu_atmel_mem_nb_data); | |
return true; | |
} | |
static void udi_dfu_atmel_mem_check(void) | |
{ | |
uint8_t *ptr_buf; | |
uint16_t packet_size; | |
while (udi_dfu_atmel_mem_nb_data) { | |
// Compute buffer to read | |
packet_size = min(udi_dfu_atmel_mem_nb_data, | |
DFU_ATMEL_BUF_TRANS_SIZE); | |
udi_dfu_atmel_mem_nb_data -= packet_size; | |
// Fill buffer from memory | |
udi_dfu_atmel_mem_sel.fnct_read(udi_dfu_atmel_buf_trans, | |
udi_dfu_atmel_mem_add, packet_size); | |
// Check buffer content | |
ptr_buf = udi_dfu_atmel_buf_trans; | |
while (packet_size--) { | |
if (*ptr_buf++ != 0xFF) { | |
// Error, don't stall request but: | |
// Update DFU status | |
udi_dfu_atmel_status.bStatus = DFU_STATUS_ERRCHECK_ERASED; | |
// Send last address checked in next Upload command | |
udi_dfu_atmel_upload_callback = udi_dfu_atmel_mem_send_last_add; | |
return; | |
} | |
udi_dfu_atmel_mem_add++; | |
} | |
} | |
} | |
static bool udi_dfu_atmel_mem_send_last_add(void) | |
{ | |
// Send last checked address | |
udi_dfu_atmel_buf_trans[0] = (uint8_t)(udi_dfu_atmel_mem_add>>8); | |
udi_dfu_atmel_buf_trans[1] = (uint8_t)udi_dfu_atmel_mem_add; | |
// Init buffer to transfer | |
udd_set_setup_payload( udi_dfu_atmel_buf_trans, 2); | |
return true; | |
} | |
static bool udi_dfu_atmel_mem_write(void) | |
{ | |
uint8_t padding_prefix; | |
Assert(udi_dfu_atmel_mem_nb_data <= DFU_ATMEL_BUF_TRANS_SIZE); | |
Assert(udi_dfu_atmel_mem_nb_data == udd_g_ctrlreq.payload_size); | |
// In order to be in accordance with the memory write entity (page size), | |
// X non-significant bytes may be added before the first byte to program. | |
// The X number is calculated to align the beginning of the firmware | |
// with the memory write entity. | |
padding_prefix = Get_align(udi_dfu_atmel_mem_add, | |
USB_DEVICE_EP_CTRL_SIZE); | |
// Program data in memory | |
udi_dfu_atmel_mem_sel.fnct_write | |
(udi_dfu_atmel_mem_add, | |
udi_dfu_atmel_buf_trans + padding_prefix, | |
udi_dfu_atmel_mem_nb_data); | |
// Init callback called after buffer filled | |
udd_g_ctrlreq.over_under_run = NULL; | |
return true; | |
} | |
//@} |