/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* uploader for IOMCU partly based on px4io_uploader.cpp from px4 */ /**************************************************************************** * * Copyright (c) 2012-2015 PX4 Development Team. All rights reserved. * * 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. Neither the name PX4 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. * ****************************************************************************/ #include #if HAL_WITH_IO_MCU #include "AP_IOMCU.h" #include #include #define debug(fmt, args ...) do { hal.console->printf("IOMCU: " fmt "\n", ## args); } while(0) extern const AP_HAL::HAL &hal; /* upload a firmware to the IOMCU */ bool AP_IOMCU::upload_fw(const char *filename) { uint32_t fw_size; fw = AP_ROMFS::find_file(filename, fw_size); // set baudrate for bootloader uart.begin(115200, 256, 256); bool ret = false; /* look for the bootloader for 150 ms */ for (uint8_t i = 0; i < 15; i++) { ret = sync(); if (ret) { break; } hal.scheduler->delay(10); } if (!ret) { debug("IO update failed sync"); return false; } uint32_t bl_rev; ret = get_info(INFO_BL_REV, bl_rev); if (!ret) { debug("Err: failed to contact bootloader"); return false; } if (bl_rev > BL_REV) { debug("Err: unsupported bootloader revision %u", unsigned(bl_rev)); return false; } debug("found bootloader revision: %u", unsigned(bl_rev)); ret = erase(); if (!ret) { debug("erase failed"); return false; } ret = program(fw_size); if (!ret) { debug("program failed"); return false; } if (bl_rev <= 2) { ret = verify_rev2(fw_size); } else { ret = verify_rev3(fw_size); } if (!ret) { debug("verify failed"); return false; } ret = reboot(); if (!ret) { debug("reboot failed"); return false; } debug("update complete"); // sleep for enough time for the IO chip to boot hal.scheduler->delay(100); return true; } /* receive a byte from the IO bootloader */ bool AP_IOMCU::recv_byte_with_timeout(uint8_t *c, uint32_t timeout_ms) { uint32_t start = AP_HAL::millis(); do { int16_t v = uart.read(); if (v >= 0) { *c = uint8_t(v); return true; } hal.scheduler->delay_microseconds(50); } while (AP_HAL::millis() - start < timeout_ms); return false; } /* receive multiple bytes from the bootloader */ bool AP_IOMCU::recv_bytes(uint8_t *p, uint32_t count) { bool ret = true; while (count--) { ret = recv_byte_with_timeout(p++, 5000); if (!ret) { break; } } return ret; } /* discard any pending bytes */ void AP_IOMCU::drain(void) { uint8_t c; bool ret; do { ret = recv_byte_with_timeout(&c, 40); } while (ret); } /* send a byte to the bootloader */ bool AP_IOMCU::send(uint8_t c) { if (uart.write(c) != 1) { return false; } return true; } /* send a buffer to the bootloader */ bool AP_IOMCU::send(const uint8_t *p, uint32_t count) { bool ret = true; while (count--) { ret = send(*p++); if (!ret) { break; } } return ret; } /* wait for bootloader protocol sync */ bool AP_IOMCU::get_sync(uint32_t timeout_ms) { uint8_t c[2]; bool ret; ret = recv_byte_with_timeout(c, timeout_ms); if (!ret) { return false; } ret = recv_byte_with_timeout(c + 1, timeout_ms); if (!ret) { return ret; } if ((c[0] != PROTO_INSYNC) || (c[1] != PROTO_OK)) { debug("bad sync 0x%02x,0x%02x", c[0], c[1]); return false; } return true; } /* drain then get sync with bootloader */ bool AP_IOMCU::sync() { drain(); /* complete any pending program operation */ for (uint32_t i = 0; i < (PROG_MULTI_MAX + 6); i++) { send(0); } send(PROTO_GET_SYNC); send(PROTO_EOC); return get_sync(); } /* get bootloader version */ bool AP_IOMCU::get_info(uint8_t param, uint32_t &val) { bool ret; send(PROTO_GET_DEVICE); send(param); send(PROTO_EOC); ret = recv_bytes((uint8_t *)&val, sizeof(val)); if (!ret) { return ret; } return get_sync(); } /* erase IO firmware */ bool AP_IOMCU::erase() { debug("erase..."); send(PROTO_CHIP_ERASE); send(PROTO_EOC); return get_sync(10000); } /* send new firmware to bootloader */ bool AP_IOMCU::program(uint32_t fw_size) { bool ret = false; uint32_t sent = 0; if (fw_size & 3) { return false; } debug("programming %u bytes...", (unsigned)fw_size); while (sent < fw_size) { /* get more bytes to program */ uint32_t n = fw_size - sent; if (n > PROG_MULTI_MAX) { n = PROG_MULTI_MAX; } send(PROTO_PROG_MULTI); send(n); send(&fw[sent], n); send(PROTO_EOC); ret = get_sync(1000); if (!ret) { debug("Failed at %u", (unsigned)sent); return false; } sent += n; } debug("upload OK"); return true; } /* verify firmware for a rev2 bootloader */ bool AP_IOMCU::verify_rev2(uint32_t fw_size) { ssize_t count; bool ret; size_t sent = 0; debug("verify..."); send(PROTO_CHIP_VERIFY); send(PROTO_EOC); ret = get_sync(); if (!ret) { return ret; } while (sent < fw_size) { /* get more bytes to verify */ uint32_t n = fw_size - sent; if (n > 4) { n = 4; } send(PROTO_READ_MULTI); send(n); send(PROTO_EOC); for (uint8_t i = 0; i 4) { n = 4; } /* calculate crc32 sum */ sum = crc_crc32(sum, &fw[bytes_read], n); bytes_read += n; } /* fill the rest with 0xff */ while (bytes_read < fw_size_remote) { sum = crc_crc32(sum, &fill_blank, 1); bytes_read++; } /* request CRC from IO */ send(PROTO_GET_CRC); send(PROTO_EOC); ret = recv_bytes((uint8_t *)(&crc), sizeof(crc)); if (!ret) { debug("did not receive CRC checksum"); return ret; } /* compare the CRC sum from the IO with the one calculated */ if (sum != crc) { debug("CRC wrong: received: 0x%x, expected: 0x%x", (unsigned)crc, (unsigned)sum); return false; } crc_is_ok = true; return true; } /* reboot IO MCU */ bool AP_IOMCU::reboot() { send(PROTO_REBOOT); hal.scheduler->delay(200); send(PROTO_EOC); hal.scheduler->delay(200); return true; } #endif // HAL_WITH_IO_MCU