/*
 * This file is part of the Black Magic Debug project.
 *
 * Copyright (C) 2011  Black Sphere Technologies Ltd.
 * Written by Gareth McMullin <gareth@blacksphere.co.nz>
 *
 * 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 <http://www.gnu.org/licenses/>.
 */
#include <string.h>

#ifdef WIN32
#   include <windows.h>
#   include <lusb0_usb.h>
#else
#   include <unistd.h>
#   include <usb.h>
#endif

#include "dfu.h"
#include "stm32mem.h"

#define STM32_CMD_GETCOMMANDS		0x00
#define STM32_CMD_SETADDRESSPOINTER	0x21
#define STM32_CMD_ERASE			0x41

static int stm32_download(usb_dev_handle *dev, uint16_t iface, 
			  uint16_t wBlockNum, void *data, int size)
{
	dfu_status status;
	int i;

	if((i = dfu_dnload(dev, iface, wBlockNum, data, size)) < 0) return i;
	while(1) {
		if((i = dfu_getstatus(dev, iface, &status)) < 0) return i;
		switch(status.bState) {
		case STATE_DFU_DOWNLOAD_BUSY:
#ifdef WIN32
			Sleep(status.bwPollTimeout);
#else
			usleep(status.bwPollTimeout * 1000);
#endif
			break;
		case STATE_DFU_DOWNLOAD_IDLE:
			return 0;
		default:	
			return -1;
		}
	}
}

int stm32_mem_erase(usb_dev_handle *dev, uint16_t iface, uint32_t addr)
{
	uint8_t request[5];

	request[0] = STM32_CMD_ERASE;
	memcpy(request+1, &addr, sizeof(addr));

	return stm32_download(dev, iface, 0, request, sizeof(request));
}

int stm32_mem_write(usb_dev_handle *dev, uint16_t iface, void *data, int size, uint32_t addr)
{
	uint8_t request[5];

	request[0] = STM32_CMD_SETADDRESSPOINTER;
	memcpy(request+1, &addr, sizeof(addr));
	stm32_download(dev, iface, 0, request, sizeof(request));
	return stm32_download(dev, iface, 2, data, size);
}

int stm32_mem_manifest(usb_dev_handle *dev, uint16_t iface)
{
	dfu_status status;
	int i;

	if((i = dfu_dnload(dev, iface, 0, NULL, 0)) < 0) return i;
	while(1) {
		if((i = dfu_getstatus(dev, iface, &status)) < 0) return 0;
#ifdef WIN32
		Sleep(status.bwPollTimeout);
#else
		usleep(status.bwPollTimeout * 1000);
#endif
		switch(status.bState) {
		case STATE_DFU_MANIFEST:
			return 0;
		default:	
			return -1;
		}
	}
}