CVS: examples/tools/bsl_c BSL.h, NONE, 1.1 BSLBackend.h, NONE, 1.1 BSLTargetMSP430.c, NONE, 1.1 BSLTargetMSP430.h, NONE, 1.1 README.txt, NONE, 1.1 SerialBSL.c, NONE, 1.1 SerialBSL.h, NONE, 1.1 config.h, NONE, 1.1 hexloader.c, NONE, 1.1 hexloader.h, NONE, 1.1 makefile, NONE, 1.1 msp430-bsl-demo.c, NONE, 1.1

Chris Liechti <[email protected]> Tue, 01 Jan 2008 14:10:46 -0800
Newsgroups gmane.comp.hardware.texas-instruments.msp430.gcc.cvs
Message-ID <[email protected]>
Update of /cvsroot/mspgcc/examples/tools/bsl_c
In directory sc8-pr-cvs16.sourceforge.net:/tmp/cvs-serv30490/tools/bsl_c

Added Files:
	BSL.h BSLBackend.h BSLTargetMSP430.c BSLTargetMSP430.h 
	README.txt SerialBSL.c SerialBSL.h config.h hexloader.c 
	hexloader.h makefile msp430-bsl-demo.c 
Log Message:
demo code of a simple BSL downloader in C

--- NEW FILE: BSL.h ---
#ifndef BSL_H
#define BSL_H

/** @file
 *
 * Constants for the BSL protocol for the MSP430.
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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.
 */

// interesting values
#define BSL_MAX_BLOCK_SIZE  240     ///< Max number of data bytes in a packet

// special symbols
#define BSL_SYNC            0x80    ///< Serial synchronization character

// answers
#define BSL_CMD_FAILED      0x70    ///< Command could not be executed successfully
#define BSL_DATA_FRAME      0x80    ///< Acknowledge, data follows
#define BSL_DATA_ACK        0x90    ///< Simple acknowledge
#define BSL_DATA_NAK        0xA0    ///< Command not accepted

// commands for the MSP430 target
#define BSL_TXPWORD         0x10    ///< Receive password to unlock commands
#define BSL_TXBLK           0x12    ///< Transmit block to boot loader
#define BSL_RXBLK           0x14    ///< Receive  block from boot loader
#define BSL_ERASE           0x16    ///< Erase one segment
#define BSL_MERAS           0x18    ///< Erase complete FLASH memory
#define BSL_CHANGEBAUD      0x20    ///< Change baudrate
#define BSL_LOADPC          0x1A    ///< Load PC and start execution
#define BSL_ERASE_CHECK     0x1C    ///< Erase check of flash
#define BSL_TXVERSION       0x1E    ///< Get BSL version

#endif // BSL_H

--- NEW FILE: BSLBackend.h ---
#ifndef BSLBACKEND_H_
#define BSLBACKEND_H_

/** @file
 *
 * Defnition of a backend for the BSL. A backend implements the communication
 * with the target and is called by the frontend that constructs the BSL frames
 * itself.
 *
 * This enables to implement different backends not only the standard serial
 * one. Examples could be tunneling BSL compatible packets through 2 or 3 wire
 * busses or TCP/IP.
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdbool.h>
#include <stdint.h>

/**
 * Execute one BSL command. The backend implements the BSL communication
 * for sending a command and receiving the answer.
 * 
 * @param command       [in] command id
 * @param message       [in] outgoing message
 * @param len           [in] outgoing message length
 * @param answer        [out] buffer for the answer. Can be NULL if no answer
 *                      is expected
 * @param answer_len    [in] size of answer buffer. Must be 0 if answer is NULL.
 * @return      number of bytes read into the answer buffer, negative numbers
 *              indicate a failure.
 */
typedef int (*BSLBackend_command_function_t)(uint8_t command,
                                             const void *message,
                                             uint8_t len,
                                             void *answer,
                                             uint8_t answer_len);


/**
 * Initialize backend.
 * 
 * @param device        [in] configuration parameter for the backend. Usualy
 *                      a device name to communicate with.
 * @return      true on success
 */
typedef bool (*BSLBackend_init_function_t)(const char *device);


/**
 * Close the communication port.
 */
typedef bool (*BSLBackend_close_function_t)(void);


/**
 * This function is called by the programmer to indicate the progress in
 * memory read and write functions.
 *
 * The implementation can either use the 'difference' to count bytes on
 * its own or '100*count/total' to calculate the progress in percent.
 * 
 * @param difference    [in] number of units processed since last call
 * @param count         [in] position whithin current block
 * @param total         [in] size of the current block
 */
typedef void (*BSLBackend_progress_function_t)(uint16_t difference,
                                               uint16_t count,
                                               uint16_t total);


/**
 * Structure describing the backend.
 */
typedef struct {
    BSLBackend_init_function_t         init;    ///< called to initialize connection
    BSLBackend_close_function_t        close;   ///< called to terminate connecion
    BSLBackend_command_function_t      command; ///< BSL commands are transfered by this function
    BSLBackend_progress_function_t     progress;///< callback installed by the user
} BSLBackend_backend_t;

#endif // BSLBACKEND_H_

--- NEW FILE: BSLTargetMSP430.c ---
/** @file
 *
 * Lowlevel BSL frontend. The functions here rely on the backend->command.
 * The functions here provide:
 * - memory read, write and erase
 * - reset and execute
 * - BSL password, version
 *
 * Refer to the header file for API documentation (for these parts that are
 * not documented in this file).
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "BSL.h"
#include "BSLTargetMSP430.h"

// - - - - - - - - - low level interface - - - - - - - - -

/** The one and only, static backend, used by the functions here. */
static BSLBackend_backend_t *bsl_backend;


/** Remebers is the patch was loaded or not. */
static bool bsl_patch_loaded = false;


/**
 * Header for BSL frames. Packed struct of bytes only, so that endian-ness of
 * host has no influence.
 */
typedef struct __attribute__((packed)) {
    uint8_t address_low;
    uint8_t address_high;
    uint8_t length_low;
    uint8_t length_high;
} MSP430_HEADER;


/**
 * Frame for memory writes. Packed struct of bytes only, so that endian-ness of
 * host has no influence.
 */
typedef struct __attribute__((packed)) {
    MSP430_HEADER header;
    uint8_t data[BSL_MAX_BLOCK_SIZE];
} MSP430_MEMORY_WRITE;


/**
 * Frame to write password. Packed struct of bytes only, so that endian-ness of
 * host has no influence.
 */
typedef struct __attribute__((packed)) {
    MSP430_HEADER header;
    uint8_t data[32];
} MSP430_PASSWORD;


/** Default password, i.e. after mass erase: 32*0xff */
static const char DEFAULT_PASSWORD[32] = {
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};


/**
 * Transfer a single data block to the MSP430 (size is limited by the protocol).
 *
 * @param address       [in] address to write the data to
 * @param buffer        [in] the data itself
 * @param length        [in] length of the data
 * @return True if the write was acknowledged by the target.
 */
static bool BSLTargetMSP430_write_block(uint16_t address, const void *buffer, uint16_t length) {
    MSP430_MEMORY_WRITE memwrite_blk = {
        .header = {
            .address_low =  address,
            .address_high = address >> 8,
            .length_low =   length,
            .length_high =  length >> 8
        }
    };
    int answer;
    if (length > BSL_MAX_BLOCK_SIZE) {
        // block too large
        return false;
    }
    memcpy(memwrite_blk.data, buffer, length);
    if (bsl_patch_loaded) BSLTargetMSP430_execute(0x0220);
    answer = bsl_backend->command(
        BSL_TXBLK,
        &memwrite_blk,
        sizeof(MSP430_HEADER) + length,
        NULL,
        0
    );
    return (answer == 0);
}


/**
 * Transfer a single data block from the MSP430 (size is limited by the
 * protocol).
 *
 * @param address       [in] address to read the data from
 * @param buffer        [out] space for the data itself
 * @param length        [in] length of the data
 * @return True if the exact number of "length" bytes was successfully read
 *         from the target.
 */
static bool BSLTargetMSP430_read_block(uint16_t address, void *buffer, uint16_t length) {
    MSP430_HEADER memread_blk = {
        .address_low =  address,
        .address_high = address >> 8,
        .length_low =   length,
        .length_high =  length >> 8
    };
    int answer;
    if (length > BSL_MAX_BLOCK_SIZE) {
        // block too large
        return false;
    }
    if (bsl_patch_loaded) BSLTargetMSP430_execute(0x0220);
    answer = bsl_backend->command(
        BSL_RXBLK,
        &memread_blk,
        sizeof(MSP430_HEADER),
        buffer,
        length
    );
    return (answer == length);
}

// - - - - - - - - - high level interface - - - - - - - - -

bool BSLTargetMSP430_init(BSLBackend_backend_t *backend, const char *device) {
    bsl_backend = backend;
    // all function pointers must be valid
    if (bsl_backend->command != NULL
     && bsl_backend->init != NULL
     && bsl_backend->close != NULL) {
        // try to initialize backend
        return bsl_backend->init(device);
    }
    return false;
}


bool BSLTargetMSP430_close(void) {
    if (bsl_backend->close != NULL) {
        return bsl_backend->close();
    }
    return false;
}


bool BSLTargetMSP430_memory_write(uint16_t address, const void *buffer, uint16_t length) {
    unsigned int count = 0;
    const char *current_buffer = buffer;
    while (count < length) {
        // calculate block size
        unsigned int size = length - count;
        if (size > BSL_MAX_BLOCK_SIZE) size = BSL_MAX_BLOCK_SIZE;
        // write one block
        if (!BSLTargetMSP430_write_block(address, current_buffer, size)) {
            // retry once if it fails the first time
            if (!BSLTargetMSP430_write_block(address, current_buffer, size)) {
                return false;
            }
        }
        count += size;
        address += size;
        current_buffer += size;
        // if a callback is registered use it to inform about the progress
        if (bsl_backend->progress != NULL) {
            bsl_backend->progress(size, count, length);
        }
    }
    return true;
}


bool BSLTargetMSP430_memory_read(uint16_t address, void *buffer, uint16_t length) {
    unsigned int count = 0;
    char *current_buffer = buffer;
    while (count < length) {
        // calculate block size
        unsigned int size = length - count;
        if (size > BSL_MAX_BLOCK_SIZE) size = BSL_MAX_BLOCK_SIZE;
        // read one block
        if (!BSLTargetMSP430_read_block(address, current_buffer, size)) {
            // retry once if it fails the first time
            if (!BSLTargetMSP430_read_block(address, current_buffer, size)) {
                return false;
            }
        }
        count += size;
        address += size;
        current_buffer += size;
        // if a callback is registered use it to inform about the progress
        if (bsl_backend->progress != NULL) {
            bsl_backend->progress(size, count, length);
        }
    }
    return true;
}


bool BSLTargetMSP430_mass_erase(void) {
    MSP430_HEADER erase_blk = {
        .address_low =  0xfe,
        .address_high = 0xff,
        .length_low =   0x06,
        .length_high =  0xa5
    };
    int answer;

    answer = bsl_backend->command(
        BSL_MERAS,
        &erase_blk,
        sizeof(MSP430_HEADER),
        NULL,
        0
    );
    return (answer == 0);
}


bool BSLTargetMSP430_execute(uint16_t address) {
    MSP430_HEADER loadpc_blk = {
        .address_low = address,
        .address_high = address >> 8,
        .length_low = 0,
        .length_high = 0
    };
    int answer;
    answer = bsl_backend->command(
        BSL_LOADPC,
        &loadpc_blk,
        sizeof(MSP430_HEADER),
        NULL,
        0
    );
    return (answer == 0);
}


bool BSLTargetMSP430_reset(void) {
    // execute reset by causing a watchdog reset WDTCNTCL|WDTPW
    return BSLTargetMSP430_write_block(0x0120, "\x08\x5a", 2);
}


bool BSLTargetMSP430_password(const char *password) {
    MSP430_PASSWORD password_blk = {{0,0,0,0}};
    int answer;
    if (password == NULL) password = DEFAULT_PASSWORD;
    memcpy(password_blk.data, password, 32);
    answer = bsl_backend->command(
        BSL_TXPWORD,
        &password_blk,
        sizeof(MSP430_HEADER) + 32,
        NULL,
        0
    );
    return (answer == 0);
}


bool BSLTargetMSP430_version(void *buffer) {
    // 1st try: read version with BSL_TXVERSION command
    MSP430_HEADER version_blk = {0,0,0,0};
    int answer;
    answer = bsl_backend->command(
        BSL_TXVERSION,
        &version_blk,
        sizeof(MSP430_HEADER),
        buffer,
        16
    );
    if (16 != answer) {
        // 2nd try: read version with memory read
        // will only work after password has been transmitted
        return BSLTargetMSP430_read_block(0x0ff0, buffer, 16);
    }
    return true;
}


/**
 * The program snippet (patch) from the BSL application note from TI.
 * It is required to correctly program devices with the ROM-BSL V1.10.
 */
static const uint8_t PATCH[] = {//@0220
    0x31, 0x40, 0x1A, 0x02, 0x09, 0x43, 0xB0, 0x12, 0x2A, 0x0E, 0xB0, 0x12,
    0xBA, 0x0D, 0x55, 0x42, 0x0B, 0x02, 0x75, 0x90, 0x12, 0x00, 0x1F, 0x24,
    0xB0, 0x12, 0xBA, 0x02, 0x55, 0x42, 0x0B, 0x02, 0x75, 0x90, 0x16, 0x00,
    0x16, 0x24, 0x75, 0x90, 0x14, 0x00, 0x11, 0x24, 0xB0, 0x12, 0x84, 0x0E,
    0x06, 0x3C, 0xB0, 0x12, 0x94, 0x0E, 0x03, 0x3C, 0x21, 0x53, 0xB0, 0x12,
    0x8C, 0x0E, 0xB2, 0x40, 0x10, 0xA5, 0x2C, 0x01, 0xB2, 0x40, 0x00, 0xA5,
    0x28, 0x01, 0x30, 0x40, 0x42, 0x0C, 0x30, 0x40, 0x76, 0x0D, 0x30, 0x40,
    0xAC, 0x0C, 0x16, 0x42, 0x0E, 0x02, 0x17, 0x42, 0x10, 0x02, 0xE2, 0xB2,
    0x08, 0x02, 0x14, 0x24, 0xB0, 0x12, 0x10, 0x0F, 0x36, 0x90, 0x00, 0x10,
    0x06, 0x28, 0xB2, 0x40, 0x00, 0xA5, 0x2C, 0x01, 0xB2, 0x40, 0x40, 0xA5,
    0x28, 0x01, 0xD6, 0x42, 0x06, 0x02, 0x00, 0x00, 0x16, 0x53, 0x17, 0x83,
    0xEF, 0x23, 0xB0, 0x12, 0xBA, 0x02, 0xD3, 0x3F, 0xB0, 0x12, 0x10, 0x0F,
    0x17, 0x83, 0xFC, 0x23, 0xB0, 0x12, 0xBA, 0x02, 0xD0, 0x3F, 0x18, 0x42,
    0x12, 0x02, 0xB0, 0x12, 0x10, 0x0F, 0xD2, 0x42, 0x06, 0x02, 0x12, 0x02,
    0xB0, 0x12, 0x10, 0x0F, 0xD2, 0x42, 0x06, 0x02, 0x13, 0x02, 0x38, 0xE3,
    0x18, 0x92, 0x12, 0x02, 0xBF, 0x23, 0xE2, 0xB3, 0x08, 0x02, 0xBC, 0x23,
    0x30, 0x41
};

bool BSLTargetMSP430_activate_patch(void) {
    // download the patch, it will be executed later, on each memory read and
    // write access
    if (BSLTargetMSP430_write_block(0x0220, &PATCH, sizeof(PATCH))) {
        bsl_patch_loaded = true;
        return true;
    } else {
        return false;
    }
}

--- NEW FILE: BSLTargetMSP430.h ---
#ifndef BSLTARGETMSP430_H
#define BSLTARGETMSP430_H

/** @file
 *
 * Lowlevel BSL frontend. The functions here rely on the 'command' function
 * implemented by the backend. The functions here provide:
 * - memory read, write and erase
 * - reset and execute
 * - BSL password, version
 *
 * There is some support for the patch that is required for ROM-BSL V1.10.
 * However the user of this code has to take care that the version is detected
 * and the patch is loaded (call ::BSLTargetMSP430_activate_patch) and that he
 * does that whenever needed to trace if the patch is still ready (action may
 * be required after executing code or reset and reconnect, etc.).
 *
 * Also do need ROM-BSL V1.30 and older a fix for the stack pointer after first
 * connect. This must also be done by the user, it is not done here.
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdbool.h>
#include <stdint.h>
#include "BSLBackend.h"

/**
 * Initialize the backend with the given parameter.
 *
 * @param backend       [in] pointer to backend structure
 * @param device        [in] string descibing the device that is used to
 *                      communicate with the target. The string is directly
 *                      passed to the backends init function.
 * @return      true on success
 */
bool BSLTargetMSP430_init(BSLBackend_backend_t *backend, const char *device);


/**
 * Close the backend.
 *
 * @return      true on success
 */
bool BSLTargetMSP430_close(void);


/**
 * Setting the password on the target to gain full access.
 * 
 * @param password        [in] buffer with 32 bytes containing the password.
 *                        If NULL is passed, the default password is supplied.
 * @return      true on success
 */
bool BSLTargetMSP430_password(const char *password);


/**
 * Get the BSL version. The buffer must be at least 16 bytes large.
 * The data also contains the MSP430 family and other data. Refer to the
 * application notes about the BSL from TI.
 *
 * Examples: <pre>
 *  printf("BSL version: %x.%02x", version[10], version[11]);
 *  printf("Device ID: %02x%02x", version[0], version[1]);
 *  uint16_t bsl_version = (version[10] << 8) | version[11];
 *  if (bsl_version <= 0x0130) ..ensure that Sp is fixed..
 *  if (bsl_version <= 0x0110) ..ensure that patch is activated..
 * </pre>
 *
 * @param buffer        [in] buffer with 16 bytes space
 * @return      true on success
 */
bool BSLTargetMSP430_version(void *buffer);


/**
 * Write buffer to target memory at given address.
 *
 * @note        It may be needed that the address and data length must be of
 *              even length. The caller has to take care of padding in that
 *              case (depends on BSL, however padding is always OK).
 *
 * @param address       [in] target address
 * @param buffer        [in] buffer source data
 * @param length        [in] length size of data
 * @return      true on success
 */
bool BSLTargetMSP430_memory_write(uint16_t address, const void *buffer, uint16_t length);


/**
 * Read from target memory info buffer.
 *
 * @note        It may be needed that the address and data length must be of
 *              even numbers (depends on BSL).
 *
 * @param address       [in] target source address
 * @param buffer        [in] destination buffer data, must be at least ::length
 *                      bytes long.
 * @param length        [in] size of data
 * @return      true on success
 */
bool BSLTargetMSP430_memory_read(uint16_t address, void *buffer, uint16_t length);


/**
 * Erase complete target Flash memory.
 * 
 * @return      true on success
 */
bool BSLTargetMSP430_mass_erase(void);


/**
 * Start executing at given address.
 *
 * This exits the BSL on the target. It is possible that target returns to the
 * BSL but depends on the code that is started. Use with care.
 * 
 * @param address       [in] target source address
 * @return      true on success
 */
bool BSLTargetMSP430_execute(uint16_t address);


/**
 * Reset target. This exits the BSL on the target.
 *
 * @note        It can happen that the target does not acknowledge this
 *              command, thus the function returns false, despite that the
 *              target actualy resetted. (seems to happen on old BSL
 *              implementations, looks like they do not access the WDTCTL
 *              register correctly and cause the reset immediately instad of
 *              delayed)
 * 
 * @note        The BSL is exit when this command is run.
 *
 * @return      true on success
 */
bool BSLTargetMSP430_reset(void);


/**
 * Calling this function installs the patch for buggy BSLs. It also sets
 * a flag so that the patch is activated on each memory access.
 *
 * @return      true on success
 */
bool BSLTargetMSP430_activate_patch(void);

#endif // BSLTARGETMSP430_H

--- NEW FILE: README.txt ---
============================
 MSP430 BSL downloader demo
============================

Overview
========
>From time to time is there a question for a C based implementation of a
programmer for the MSP430 using the Boot Strap Loader (BSL). Here it is :-)

Features and facts:
- BSD licensed well documented code
- flexible backends (other than plain serial can be implemented)
- supports Intel HEX compatible files (only)

TODO:
- baudrate setting (and download alternative BSLs)
- support non posix hosts (Windows w/o cygwin)
- support more binary formats, TI-Text


Note: While the C implementation may be the better choice in some cases, the
Python implementation of msp430-bsl that is available from http://mspgcc.sf.net
is more flexible and has more features. Consider using that one if possible.


Build instructions
==================

Use the makefile, i.e. invoke the following commands on a command prompt:
- ``make clean``
- ``make``

To change the amount of debug messages, edit ``config.h`` and recompile
(including clean).


Usage
=====
``msp430-bsl-demo /dev/ttyS0 ../../leds/leds.a43``

Currently there are any other options. Some settings can be adjusted in the
source code, for example the use of RTS and DTR for RESET and TEST can be
altered.


Availability
============
You've probably got this from http://mspgcc.sf.net. It's home is there.

License (BSD)::

  Copyright (c) 2007, Chris Liechti <[email protected]>

  All rights reserved.

  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 the authors or copyright holders 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.

--- NEW FILE: SerialBSL.c ---
/** @file
 *
 * BSL backend implementation using the serial port. This is the usual way
 * to use the built in ROM-BSL of MSP430 devices.
 *
 * This is an implementation for posix systems.
 *
 * Additional to the functions a BSLBackend must implement are functions
 * supplied that modifiy the TEST and RESET pins (if the hardware adapter has
 * them wired to the serial port control lines RTS and DTR).
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#include "config.h"
#include "BSL.h"
#include "BSLBackend.h"

//---------------------------------------------------------------------------
static int fd = -1; ///< file descriptor for the serial port


/**
 * Read a single character from the port.
 * 
 * @return  character (0...255) or -1 on error or timeout
 */
int SerialBSL_read(void) {
    int n;
    fd_set input;
    struct timeval timeout;
    uint8_t data;

    // initialize the input set for select
    FD_ZERO(&input);
    FD_SET(fd, &input);

    // initialize the timeout structure for select
    timeout.tv_sec  = 2;
    timeout.tv_usec = 0;

    // use select to wait for input
    n = select(fd+1, &input, NULL, NULL, &timeout);

    // see if there was an error or actual data
    if (n < 0) {
        BSL_Error("select failed");
    } else if (n == 0) {
        BSL_Error("TIMEOUT");
    } else {
        // we have input
        if (FD_ISSET(fd, &input)) {
            read(fd, &data, 1);
            BSL_Trace("read: 0x%02x", data);
            return data;
        }
    }
    return -1;
}


/**
 * Write a single character to the port.
 * 
 * @return  >= 0 on success or -1 on error or timeout
 */
int SerialBSL_write(int character) {
    int n = 0;
    uint8_t data = character;
    BSL_Trace("write: 0x%02x", data);
    n = write(fd, &data, 1);
    if (n < 0) {
        return -1;
    }
    return n;
}


//---------------------------------------------------------------------------

bool SerialBSL_invert_test = false;        ///< invert the TEST/TCK signal
bool SerialBSL_invert_reset = false;       ///< invert the RESET signal
bool SerialBSL_swap_test_reset = false;    ///< swap the serial port control lines


/**
 * Modify the serial port control line connected to the TEST or TCK pin of the
 * MSP430 (depending on derrivate).
 *
 * @param level         [in] low or high
 */
static void SerialBSL_TEST(bool level) {
    int bitmask = SerialBSL_swap_test_reset ? TIOCM_DTR : TIOCM_RTS;
    if (ioctl(fd, (SerialBSL_invert_test ? !level : level) ? TIOCMBIS : TIOCMBIC, &bitmask) != 0) {
        BSL_Error("ioctl failed");
    }
    usleep(10000);
}


/**
 * Modify the serial port control line connected to the RESET pin of the MSP430.
 *
 * @param level         [in] low or high
 */
static void SerialBSL_RESET(bool level) {
    int bitmask = SerialBSL_swap_test_reset ? TIOCM_RTS : TIOCM_DTR;
    if (ioctl(fd, (SerialBSL_invert_reset ? !level : level) ? TIOCMBIS : TIOCMBIC, &bitmask) != 0) {
        BSL_Error("ioctl failed");
    }
    usleep(10000);
}


/**
 * Generate the standard pulse sequence to invoke the ROM-BSL.
 */
void SerialBSL_invoke_ROM_BSL(void) {  //      !TEST/TCK  RESET
    SerialBSL_RESET(true);      // power supply      |      |
    SerialBSL_TEST(true);       // power supply      |      |
    usleep(250000);             // charge            |    __|
    SerialBSL_RESET(false);     // RST  pin: GND   __|   |
    SerialBSL_TEST(false);      // TEST pin: Vcc  |__    |
    SerialBSL_TEST(true);       // TEST pin: GND   __|   |
    SerialBSL_TEST(false);      // TEST pin: Vcc  |      |__
    SerialBSL_RESET(true);      // RST  pin: Vcc  |__       |
    SerialBSL_TEST(true);       // TEST pin: GND     |      |
    usleep(250000);             // charge            |      |
    tcflush(fd, TCIFLUSH);
}

//---------------------------------------------------------------------------

/**
 * Initialize backend.
 * 
 * @param device        [in] device to do the reads and writes on
 * @return      true on success
 */
bool SerialBSL_init(const char *device) {
    struct termios newtio;

    fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd == -1) {
        // Could not open the port.
        BSL_Error("Unable to open specified device: %s", device);
        return false;
    }
    fcntl(fd, F_SETFL, 0);

    bzero(&newtio, sizeof(newtio)); // clear struct for new port settings

    // CS8     : 8 bits
    // CREAD   : enable receiving characters
    // PARENB  : enable even parity
    newtio.c_cflag = CS8 | CREAD | PARENB;
    cfsetispeed(&newtio, B9600);
    cfsetospeed(&newtio, B9600);
    
    // IGNPAR  : ignore bytes with parity errors
    // otherwise make device raw (no other input processing)
    newtio.c_iflag = IGNPAR;

    // set for raw output
    newtio.c_oflag = 0;

    // disable all echo functionality, and don't send signals
    newtio.c_lflag = 0;

    newtio.c_cc[VTIME]    = 0;   // inter-character timer is not used
    newtio.c_cc[VMIN]     = 0;   // blocking read

    // flush and activate the settings for the port
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);
    return true;
}


/**
 * Close port. It is allowed to call this function when the port has not been
 * opened previously.
 *
 * @return      true on success
 */
bool SerialBSL_close() {
    if (fd >= 0) {
        close(fd);
        fd = -1;
    }
    return true;
}


/**
 * Execute one BSL command. This backend works with the serial BSL.
 * - sync with the target (several tries)
 * - send command
 * - receive answer
 * 
 * @param command       [in] command id
 * @param message       [in] outgoing message
 * @param len           [in] outgoing message length
 * @param answer        [out] buffer for the answer. Can be NULL if no answer
 *                      is expected.
 * @param answer_len    [in] size of answer buffer. Must be 0 if answer is NULL.
 * @return      Number of bytes read into the answer buffer, negative numbers
 *              indicate a failure.
 */
int SerialBSL_command(uint8_t command, const void *message, uint8_t len, void *answer, uint8_t answer_length) {
    int b;
    uint8_t checksum_low = 0xff;
    uint8_t checksum_high = 0xff;
    uint16_t received_length;
    int answer_code;
    uint8_t answer_len1;
    uint8_t answer_len2;
    uint8_t dummy;
    uint16_t answer_checksum;

    BSL_Debug("command 0x%02x %d bytes, expect %d bytes", command, len, answer_length);

    // sync with slave, expect an acknowlege. give it three tries.
    for (b = 3; b; b--) {
        BSL_Debug("syncing...");
        SerialBSL_write(BSL_SYNC);
        answer_code = SerialBSL_read();
        if (answer_code == BSL_DATA_ACK) {
            break;
        }
    }
    // timeout (no more tries)? abort if so
    if (b <= 0) {
        BSL_Error("synchronization failed");
        return -1;
    }
  
    BSL_Debug("sync ok");
  
    // calc checksum for header
    checksum_low ^= BSL_DATA_FRAME;
    checksum_high ^= command;
    checksum_low ^= len;
    checksum_high ^= len;
    // send header
    SerialBSL_write(BSL_DATA_FRAME);
    SerialBSL_write(command);
    SerialBSL_write(len);
    SerialBSL_write(len);
    for (b = 0; b < len; b++) {
        SerialBSL_write(((uint8_t*)message)[b]);
        // update checksum
        if ((b & 1) == 0) {
            checksum_low  ^= ((uint8_t*)message)[b];
        } else {
            checksum_high ^= ((uint8_t*)message)[b];
        }
    }
    // send checkusm
    SerialBSL_write(checksum_low);
    SerialBSL_write(checksum_high);

    // read answer
    answer_code = SerialBSL_read();
    switch (answer_code) {
        case BSL_DATA_FRAME:
            BSL_Debug("DATA_FRAME");
            // read header
            b = SerialBSL_read();
            if (b < 0) {
                BSL_Error("error while reading header");
                return -1;
            }
            dummy = b;
            b = SerialBSL_read();
            if (b < 0) {
                BSL_Error("error while reading header");
                return -1;
            }
            answer_len1 = b;
            b = SerialBSL_read();
            if (b < 0) {
                BSL_Error("error while reading header");
                return -1;
            }
            answer_len2 = b;
            if (answer_len1 != answer_len2) {
                BSL_Error("error in header (lengths do not match)");
                return -1;
            }
            // read data
            for (received_length = 0; received_length < answer_len1; received_length++) {
                b = SerialBSL_read();
                if (b < 0) {
                    BSL_Error("timeout while reading answer");
                    return -1;
                }
                // save character in buffer if it is valid and has space left
                if (answer != NULL && answer_length) {
                    ((uint8_t *)answer)[received_length] = (uint8_t)b;
                    answer_length--;
                } else {
                    BSL_Error("answer buffer is too small, dropping data");
                    return -1;
                }
            }
            // read checksum
            answer_checksum = SerialBSL_read() | (SerialBSL_read() << 8);
          
            // check length of answer
            if (received_length != answer_len1) {
                BSL_Error("answer too short");
                return -1;
            }
          
            // calc checksum of answer
            checksum_low = 0xff;
            checksum_high = 0xff;
            checksum_low ^= BSL_DATA_FRAME;
            checksum_high ^= dummy;
            checksum_low ^= answer_len1;
            checksum_high ^= answer_len2;
            for (b = 0; b < answer_len1; b++) {
                if ((b & 1) == 0) {
                    checksum_low  ^= ((uint8_t*)answer)[b];
                } else {
                    checksum_high ^= ((uint8_t*)answer)[b];
                }
            }
          
            if (answer_checksum == (checksum_low | (checksum_high << 8))) {
                return received_length;
            } else {
                BSL_Error("wrong checksum %x!=%x",
                    answer_checksum,
                    (checksum_low | (checksum_high << 8))
                );
                return -1;
            }
          
        case BSL_DATA_ACK:
            BSL_Debug("DATA_ACK");
            return 0;

        case BSL_DATA_NAK:
            BSL_Debug("DATA_NAK");
            return -1;

        case BSL_CMD_FAILED:
            BSL_Debug("CMD_FAILED");
            return -1;

        case -1: // timeout
            BSL_Error("timeout");
            return -1;

        default: // unexpected answer
            BSL_Error("unexpected answer 0x%02x", answer_code);
            return -1;
    }
}


/**
 * A serial BSL backend instance. This one is global, only one can exist.
 */
BSLBackend_backend_t SerialBSL_backend_instance = {
    .init = SerialBSL_init,
    .close = SerialBSL_close,
    .command = SerialBSL_command,
};


--- NEW FILE: SerialBSL.h ---
#ifndef SERIALBSL_H
#define SERIALBSL_H

/** @file
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdbool.h>
#include "BSLBackend.h"


/**
 * A serial BSL backend instance. This one is global, only one can exist.
 */
extern BSLBackend_backend_t SerialBSL_backend_instance;

//---------------------------------------------------------------------------
// extensions

// The following variables alter the behavior of SerialBSL_invoke_ROM_BSL

extern bool SerialBSL_invert_test;        ///< invert the TEST/TCK signal
extern bool SerialBSL_invert_reset;       ///< invert the RESET signal
extern bool SerialBSL_swap_test_reset;    ///< swap the serial port control lines

/**
 * Generate the standard pulse sequence to invoke the ROM-BSL.
 */
void SerialBSL_invoke_ROM_BSL(void);


#endif // SERIALBSL_H

--- NEW FILE: config.h ---
#ifndef CONFIG_H
#define CONFIG_H

// The following macros are used to log whithin the different modules and functions.
// Define empty macros to disable logging.
// Note: variable argument macros are not supported by all compilers, however, GCC does.

#define BSL_Trace(...) ;//do { printf("%s:%u:TRACE: ", __FILE__, __LINE__); printf(__VA_ARGS__); printf(" [%s]\n", __FUNCTION__); } while (0);
#define BSL_Debug(...) ;//do { printf("%s:%u:DEBUG: ", __FILE__, __LINE__); printf(__VA_ARGS__); printf(" [%s]\n", __FUNCTION__); } while (0);
#define BSL_Info(...)  do { printf("%s:%u:INFO: ", __FILE__, __LINE__); printf(__VA_ARGS__); printf(" [%s]\n", __FUNCTION__); } while (0);
#define BSL_Error(...) do { printf("%s:%u:ERROR: ", __FILE__, __LINE__); printf(__VA_ARGS__); printf(" [%s]\n", __FUNCTION__); } while (0);

#endif //CONFIG_H

--- NEW FILE: hexloader.c ---
/** @file
 *
 * Functions to load MSP430 binaries from files. Currently there is support to
 * load intel hex compatible files.
 *
 * See also the header file for docs.
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "hexloader.h"

/**
 * Allocate the memory for a segment and its data.
 *
 * @param max_data_size         [in] size of the data memory
 * @return pointer to the segment or NULL on errors.
 */
static segment_t *allocate_segment(uint32_t max_data_size) {
    BSL_Trace("new segment %d bytes", max_data_size);
    // allocate the segment itself
    segment_t *new_segment = malloc(sizeof(segment_t));
    if (new_segment == NULL) {
        return NULL;
    }
    // allocate the data buffer for the segment
    new_segment->data = malloc(max_data_size);
    if (new_segment->data == NULL) {
        free(new_segment);
        return NULL;
    }
    // initialize some fields
    new_segment->next = NULL;
    new_segment->size = 0;
    new_segment->allocated_size = max_data_size;
    return new_segment;
}


/**
 * Parses one intel hex formated line into the corresponding fields.
 * 
 * @param line          [in]  A data record (line) of the memory
 * @param data          [out] Data bytes of the parsed record, must be at least
 *                      16 bytes wide (depending on input line length).
 * @param address       [out] The address of the parsed record
 * @param data_length   [out] The number of data bytes of the parsed record
 * @return The record type of parsed record or a negative number on errors.
 */
static int hexloader_parse_hex_line(const char *line, uint8_t *data, unsigned *address, unsigned *data_length) {
    uint8_t sum = 0;
    unsigned length, checksum;
    const char *read_position = line;
    unsigned line_type;
  
    // read the per line header
    // ihex marker
    if (*read_position++ != ':') return -1;
    // ihex line header length
    if (strlen(line) < 11) return -2;
    if (!sscanf(read_position, "%02x", &length)) return -3;
    // ihex header+data length
    if (strlen(line) < (11 + (length * 2)) ) return -4;
    sum += length;
    read_position += 2;
    // address
    if (!sscanf(read_position, "%04x", address)) return -5;
    sum += *address >> 8;
    sum += *address;
    read_position += 4;
    // line type
    if (!sscanf(read_position, "%02x", &line_type)) return -6;
    sum += line_type;
    read_position += 2;
    
    // read the data bytes
    for (*data_length = 0; *data_length < length; (*data_length)++) {
        unsigned byte;
        if (!sscanf(read_position, "%02x", &byte)) return -7; // data
        data[*data_length] = byte;
        sum += byte;
        read_position += 2;
    }
    // read and verify checksum
    if (!sscanf(read_position, "%02x", &checksum)) return -8; // checksum read error
    if ((uint8_t)(sum + (uint8_t)checksum) != 0) return -9; // checksum compare indicates data error
    return line_type;
}


/**
 * Loads the specified file into a list of segments. The list order is the same
 * as the segments occour in the file, which is usualy in increasing address
 * order.
 *
 * @param filename      [in] name of the file
 * @return Pointer to the first segment. The segments can be interated by their
 *         .next pointer.
 */
segment_t *hexloader_load_ihex(const char *filename) {
    segment_t *first_segment = NULL;
    segment_t *segment = NULL;
    FILE *fp;
    unsigned lineno = 1;
    
    BSL_Trace("hexloader_load_ihex(%s)", filename);
    
    fp = fopen(filename, "r");
    if (fp == NULL) {
        BSL_Error("Can't open file '%s' for reading", filename);
        return NULL;
    }

    while (!feof(fp) && !ferror(fp)) {
        unsigned addr, n;
        int status;
        uint8_t line_data[256];
        char line[100];
        
        // read one line and process it
        fgets(line, sizeof(line), fp);
        status = hexloader_parse_hex_line(line, line_data, &addr, &n);
        if (status == 0) { // data
            BSL_Trace("%s:%u data line", filename, lineno);
            // allocate a new segment if we don't have one yet, or if the
            // data is not contiguous
            if (segment == NULL || segment->address + segment->size != addr) {
                segment_t *new_segment = allocate_segment(1024); // start small but not too small
                if (new_segment == NULL) {
                    BSL_Error("error allocating memory for segment");
                    break;
                }
                BSL_Debug("new segment %p (%d B)", new_segment, new_segment->allocated_size);
                new_segment->address = addr;
                // chain
                if (segment != NULL) segment->next = new_segment;
                // remember first one
                if (first_segment == NULL) first_segment = new_segment;
                segment = new_segment;
            }
            // resize current segment if it is too small
            if (segment->size + n >= segment->allocated_size) {
                // resize the data block for the segment by some factor
                // (factor 2 would work too, but with 1.5 it doesn't grow that
                // fast but still good enough for most cases)
                segment->allocated_size = segment->allocated_size*1.5;
                BSL_Debug("grow segment data space %p %d B", segment, segment->allocated_size);
                // replace the data block in our segment (realloc may or may
                // not return the same memory)
                segment->data = realloc(segment->data, segment->allocated_size);
                if (segment->data == NULL) {
                    BSL_Error("realloc failed");
                    break;
                }
            }
            // store data from line into segment
            memcpy(&segment->data[addr-segment->address], line_data, n);
            segment->size += n;
        } else if (status == 1) {  // end of file
            BSL_Debug("%s:%u EOF marker", filename, lineno);
            break;
        } else if (status == 2) {  // begin of file
            BSL_Debug("%s:%u begin of file marker", filename, lineno);
        } else if (status == 3) {  // whatever..
        } else if (status < 0) {
            BSL_Error("hex file, line: %d '%s' (aborted %d)", lineno, line, status);
            break;
        } else {
            BSL_Info("ignored unknown field in hex file, line: %d (%d)", lineno, status);
        }
        lineno++;
    }
    fclose(fp);
    return first_segment;
}


/**
 * Free a chain of segements and their data, starting with the given one.
 * The function interates over the segments next pointer and frees all
 * segments and their data it encounters.
 *
 * @param segment       [in] pointer to the first segment that is freed.
 *                      The memory it points to is no longer valid and must
 *                      not be accessed again. The same applies for any
 *                      segment and data block within the chain.
 */
void hexloader_free(segment_t *segment) {
    while (segment) {
        BSL_Debug("free segment %p", segment);
        segment_t *next = segment->next;
        if (segment->data != NULL) free(segment->data);
        free(segment);
        segment = next;
    }
}


--- NEW FILE: hexloader.h ---
#ifndef HEXLOADER_H
#define HEXLOADER_H

/** @file
 *
 * Functions to load MSP430 binaries from files. Currently there is support
 * to load intel hex compatible files.
 *
 * The data is kept in a list of segments. A segment descibes a block of
 * contiguous data, located at a start address. Each segment also contains
 * a next pointer, so that several segments can be chained to a linked list.
 *
 * The implementation allocates the memory for segments dynamically using
 * malloc.
 *
 * Public interface.
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdint.h>

/**
 * This structure is used to manage segments and their data.
 */
typedef struct segment {
    struct segment *next;       ///< segements are chained as linked list. NULL means end of list.
    uint32_t address;           ///< data start address
    uint32_t size;              ///< actual data size, must be <= allocated_size
    uint32_t allocated_size;    ///< size of the allocated data space
    uint8_t *data;              ///< data space (allocated using malloc)
} segment_t;

//--------------- public functions ----------------------------------------------

/**
 * Load a list of segments from an intel hex compatible file.
 *
 * @param filename      [in] name of the hex file
 * @return The function returns a pointer to the first segment on success,
 *         NULL on errors.
 */
segment_t *hexloader_load_ihex(const char *filename);


/**
 * Free a chain of segements and their data, starting with the given one.
 */
void hexloader_free(segment_t *segment);

#endif // HEXLOADER_H

--- NEW FILE: makefile ---
CFLAGS = -O2 -Wall

OBJECTS = msp430-bsl-demo.o hexloader.o BSLTargetMSP430.o SerialBSL.o

ifdef WINDIR
    SUFFIX = .exe
endif

EXECUTABLE = msp430-bsl-demo$(SUFFIX)

$(EXECUTABLE): $(OBJECTS)
	$(CC) -o $@ $^

.PHONY: clean
clean:
	rm -f $(OBJECTS) $(EXECUTABLE)


--- NEW FILE: msp430-bsl-demo.c ---
/** @file
 *
 * Main program to load data from a file, invoke ROM-BSL and and flash a
 * MSP430.
 *
 * - - - License (BSD) - - -
 *
 * Copyright (c) 2007, Chris Liechti <[email protected]>
 * 
 * All rights reserved.
 * 
 * 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 the authors or copyright holders 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "hexloader.h"
#include "SerialBSL.h"
#include "BSLTargetMSP430.h"

/**
 * Holds the current action used for the progress
 */
static const char *progress_message = NULL;


/**
 * Total number of bytes to read and write
 */
static unsigned int progress_total = 0;


/**
 * Current bytes transfered
 */
static unsigned int progress_count = 0;


/**
 * Sets the current action for the init progress.
 * 
 * @param pMessage [in]  The action of the init progress
 */
static void set_progress_message(const char* message) {
    progress_message = message;
}


/**
 * Prints the progress for the specified action. This function is called by the 
 * backend.
 *
 * @param difference    [in] number of bytes processed since last call
 * @param count         [in] position whithin current block
 * @param total         [in] size of the current block
 */
static void bsl_progress_callback(uint16_t difference, uint16_t count, uint16_t total) {
    // if message is set, display progress in percent
    if (progress_message != NULL) {
        progress_count += difference;
        printf("\r%s: %3d%%  \r", progress_message, 100*progress_count/progress_total);
        fflush(stdout);
    }
}

//--------------- public functions --------------------------------------------

/**
 * Erase, Write, Verify, Reset an MSP430 target.
 *
 * @param segment       [in] list of segments to write
 */
bool flash_target(const segment_t *first_segment) {
    const segment_t *segment = first_segment;
    
    // iterate through the list of segments to sum the total size for the
    // progress messages in per-cent
    while (segment != NULL) {
        progress_total += segment->size;
        segment = segment->next;
    }
    // update totals: data for write and verification read -> factor 2
    progress_total *= 2;

    // erase complete flash (so we dont need to know the password)
    BSL_Info("Mass Erase...");
    if (!BSLTargetMSP430_mass_erase()) {
        BSL_Error("FAILED (erase)");
        return false;
    }
    // gain access to the other BSL functions, after the mass erase it's the
    // default password
    if (!BSLTargetMSP430_password(NULL)) {
        BSL_Error("FAILED (password)");
        return false;
    }

    // some buggy BSL versions need special treatment
    uint8_t version[16];
    if (!BSLTargetMSP430_version(&version)) {
        BSL_Error("FAILED (BSL version)");
        return false;
    } else {
        BSL_Info("BSL version: %X.%02X", version[10], version[11]);
        BSL_Info("Device ID: %02X%02X", version[0], version[1]);
        uint16_t bsl_version = (version[10] << 8) | version[11];
        
        // XXX Things that could be implemented here for completness:
        // - alternatively download an other BSL here
        // - optionally switch the baudrate here

        // check if device needs a fix for the stack pointer
        if (bsl_version <= 0x0130) {
            BSL_Info("Adjust SP. Load PC with 0x0C22...")
            if (!BSLTargetMSP430_execute(0x0C22)) {
                BSL_Error("FAILED (execute)");
                return false;
            }
            if (!BSLTargetMSP430_password(NULL)) {
                BSL_Error("FAILED (password 2)");
                return false;
            }
        }
        
        // check if device needs the patch
        if (bsl_version <= 0x0110) {
            if (!BSLTargetMSP430_activate_patch()) {
                BSL_Error("FAILED (patch)");
                return false;
            }
        }
    }
    
    BSL_Info("Download...");
    
    // writing segments
    set_progress_message("Writing");
    segment = first_segment;
    while (segment != NULL) {
        BSL_Debug("Writing segment @0x%04x %d Bytes...", segment->address, segment->size);

        if (!BSLTargetMSP430_memory_write(segment->address, segment->data, segment->size)) {
            BSL_Error("FAILED (write)");
            return false;
        }
        segment = segment->next;
    }
    
    // reading segments and compare to file, that is, verify
    set_progress_message("Verifying");
    segment = first_segment;
    while (segment != NULL) {
        BSL_Debug("Verifying segment @0x%04x %d Bytes...", segment->address, segment->size);
        void *buffer = malloc(segment->size);
        if (buffer == NULL) {
            BSL_Error("could not allocate buffer");
            break;
        }
        if (BSLTargetMSP430_memory_read(segment->address, buffer, segment->size)) {
            if (memcmp(segment->data, buffer, segment->size) != 0) {
                BSL_Error("FAILED (verify)");
                return false;
            }
        } else {
            BSL_Error("FAILED (read)");
            return false;
        }
        free(buffer);
        segment = segment->next;
    }
    // disable progress update messgages
    set_progress_message(NULL);
  
    // reset target
    BSL_Info("Resetting target...");

    if (!BSLTargetMSP430_reset()) {
        BSL_Info("FAILED (reset)");
        // ignore error
    }

    BSL_Debug("Download successful");
    return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

/**
 * Example main program that read device and hex file name from command line.
 */
int main(int argc, char *argv[]) {
    int exit_code = 1;
    fprintf(stderr, "MSP430 BSL demo programmer in C code.\n");
    fprintf(stderr, "Copyright (c) 2007, Chris Liechti <[email protected]>\n");

    if (argc != 3) {
        fprintf(stderr, "USAGE: %s device firmwarefile\n", argv[0]);
        return 1;
    }
    
    // options
    //~ SerialBSL_invert_test = true;
    //~ SerialBSL_invert_reset = true;
    //~ SerialBSL_swap_test_reset = true;

    // load the segments
    BSL_Info("loading file...");
    segment_t *first_segment = hexloader_load_ihex(argv[2]);
    if (first_segment == NULL) {
        BSL_Error("failed to load '%s'", argv[2])
        return 1;
    }
    
    // iterate through the list of segments, print an informative message
    segment_t *segment = first_segment;
    unsigned n = 1;
    unsigned total = 0;
    while (segment != NULL) {
        BSL_Info("Segment #%d 0x%04x-0x%04x (%u bytes)",
            n,
            segment->address,
            segment->address + segment->size - 1,
            segment->size
        );
        //~ hexdump(segment->data, segment->size, segment->address);
        total += segment->size;
        n++;
        segment = segment->next;
    }
    BSL_Info("Loaded total %u bytes from '%s'", total, argv[2]);

    BSL_Debug("Connecting to Target...");
    // set callback function for the progress indication
    SerialBSL_backend_instance.progress = bsl_progress_callback;
    // initialize
    if (BSLTargetMSP430_init(&SerialBSL_backend_instance, argv[1])) {
        // we are using the built in ROM-BSL started by the serial port
        // control lines.
        BSL_Info("Invoke ROM-BSL...");
        SerialBSL_invoke_ROM_BSL();
        
        // flash the loaded data
        if (flash_target(first_segment)) {
            BSL_Info("SUCCESS");
            exit_code = 0;              // success
        } else {
            BSL_Error("FAILED");
        }
    } else {
        BSL_Error("FAILED (init)");
    }
    
    // cleanup and exit
    BSLTargetMSP430_close();
    hexloader_free(segment);
    return exit_code;
}


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/