blob: 4498e5f5c3a797aa13a9ab982c82465d53602bfc [file] [log] [blame]
/**
* \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;
}
//@}