aboutsummaryrefslogtreecommitdiffstats
path: root/dediprog.c
blob: 2f5b441fe4f8c39419823bdaafd101339ad3b7bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
generated by cgit v1.2.3 (git 2.25.1) at 2025-04-07 00:43:12 +0000
 


='#n1049'>1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2010 Carl-Daniel Hailfinger
 * Copyright (C) 2015 Simon Glass
 * Copyright (C) 2015 Stefan Tauner
 *
 * 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; version 2 of the License.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

#include "platform.h"

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <libusb.h>
#include "flash.h"
#include "chipdrivers.h"
#include "programmer.h"
#include "spi.h"

/* LIBUSB_CALL ensures the right calling conventions on libusb callbacks.
 * However, the macro is not defined everywhere. m(
 */
#ifndef LIBUSB_CALL
#define LIBUSB_CALL
#endif

#define FIRMWARE_VERSION(x,y,z) ((x << 16) | (y << 8) | z)
#define DEFAULT_TIMEOUT 3000
#define DEDIPROG_ASYNC_TRANSFERS 8 /* at most 8 asynchronous transfers */
#define REQTYPE_OTHER_OUT (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_OTHER)	/* 0x43 */
#define REQTYPE_OTHER_IN (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_OTHER)	/* 0xC3 */
#define REQTYPE_EP_OUT (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT)	/* 0x42 */
#define REQTYPE_EP_IN (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT)	/* 0xC2 */
struct libusb_context *usb_ctx;
static libusb_device_handle *dediprog_handle;
static int dediprog_in_endpoint;
static int dediprog_out_endpoint;

enum dediprog_devtype {
	DEV_UNKNOWN		= 0,
	DEV_SF100		= 100,
	DEV_SF600		= 600,
};

enum dediprog_leds {
	LED_INVALID		= -1,
	LED_NONE		= 0,
	LED_PASS		= 1 << 0,
	LED_BUSY		= 1 << 1,
	LED_ERROR		= 1 << 2,
	LED_ALL			= 7,
};

/* IO bits for CMD_SET_IO_LED message */
enum dediprog_ios {
	IO1			= 1 << 0,
	IO2			= 1 << 1,
	IO3			= 1 << 2,
	IO4			= 1 << 3,
};

enum dediprog_cmds {
	CMD_TRANSCEIVE		= 0x01,
	CMD_POLL_STATUS_REG	= 0x02,
	CMD_SET_VPP		= 0x03,
	CMD_SET_TARGET		= 0x04,
	CMD_READ_EEPROM		= 0x05,
	CMD_WRITE_EEPROM	= 0x06,
	CMD_SET_IO_LED		= 0x07,
	CMD_READ_PROG_INFO	= 0x08,
	CMD_SET_VCC		= 0x09,
	CMD_SET_STANDALONE	= 0x0A,
	CMD_SET_VOLTAGE		= 0x0B,	/* Only in firmware older than 6.0.0 */
	CMD_GET_BUTTON		= 0x11,
	CMD_GET_UID		= 0x12,
	CMD_SET_CS		= 0x14,
	CMD_IO_MODE		= 0x15,
	CMD_FW_UPDATE		= 0x1A,
	CMD_FPGA_UPDATE		= 0x1B,
	CMD_READ_FPGA_VERSION	= 0x1C,
	CMD_SET_HOLD		= 0x1D,
	CMD_READ		= 0x20,
	CMD_WRITE		= 0x30,
	CMD_WRITE_AT45DB	= 0x31,
	CMD_NAND_WRITE		= 0x32,
	CMD_NAND_READ		= 0x33,
	CMD_SET_SPI_CLK		= 0x61,
	CMD_CHECK_SOCKET	= 0x62,
	CMD_DOWNLOAD_PRJ	= 0x63,
	CMD_READ_PRJ_NAME	= 0x64,
	// New protocol/firmware only
	CMD_CHECK_SDCARD	= 0x65,
	CMD_READ_PRJ		= 0x66,
};

enum dediprog_target {
	FLASH_TYPE_APPLICATION_FLASH_1	= 0,
	FLASH_TYPE_FLASH_CARD,
	FLASH_TYPE_APPLICATION_FLASH_2,
	FLASH_TYPE_SOCKET,
};

enum dediprog_readmode {
	READ_MODE_STD			= 1,
	READ_MODE_FAST			= 2,
	READ_MODE_ATMEL45		= 3,
	READ_MODE_4B_ADDR_FAST		= 4,
	READ_MODE_4B_ADDR_FAST_0x0C	= 5, /* New protocol only */
};

enum dediprog_writemode {
	WRITE_MODE_PAGE_PGM 			= 1,
	WRITE_MODE_PAGE_WRITE			= 2,
	WRITE_MODE_1B_AAI			= 3,
	WRITE_MODE_2B_AAI			= 4,
	WRITE_MODE_128B_PAGE			= 5,
	WRITE_MODE_PAGE_AT26DF041		= 6,
	WRITE_MODE_SILICON_BLUE_FPGA		= 7,
	WRITE_MODE_64B_PAGE_NUMONYX_PCM		= 8,	/* unit of 512 bytes */
	WRITE_MODE_4B_ADDR_256B_PAGE_PGM	= 9,
	WRITE_MODE_32B_PAGE_PGM_MXIC_512K	= 10,	/* unit of 512 bytes */
	WRITE_MODE_4B_ADDR_256B_PAGE_PGM_0x12	= 11,
	WRITE_MODE_4B_ADDR_256B_PAGE_PGM_FLAGS	= 12,
};

enum dediprog_standalone_mode {
	ENTER_STANDALONE_MODE = 0,
	LEAVE_STANDALONE_MODE = 1,
};

const struct dev_entry devs_dediprog[] = {
	{0x0483, 0xDADA, OK, "Dediprog", "SF100/SF600"},

	{0},
};

static int dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
enum dediprog_devtype dediprog_devicetype = DEV_UNKNOWN;

#if defined(LIBUSB_MAJOR) && defined(LIBUSB_MINOR) && defined(LIBUSB_MICRO) && \
    LIBUSB_MAJOR <= 1 && LIBUSB_MINOR == 0 && LIBUSB_MICRO < 9
/* Quick and dirty replacement for missing libusb_error_name in libusb < 1.0.9 */
const char * LIBUSB_CALL libusb_error_name(int error_code)
{
	if (error_code >= INT16_MIN && error_code <= INT16_MAX) {
		/* 18 chars for text, rest for number (16 b should be enough), sign, nullbyte. */
		static char my_libusb_error[18 + 5 + 2];
		sprintf(my_libusb_error, "libusb error code %i", error_code);
		return my_libusb_error;
	} else {
		return "UNKNOWN";
	}
}
#endif

/* Returns true if firmware (and thus hardware) supports the "new" protocol */
static bool is_new_prot(void)
{
	switch (dediprog_devicetype) {
	case DEV_SF100:
		return dediprog_firmwareversion >= FIRMWARE_VERSION(5, 5, 0);
	case DEV_SF600:
		return dediprog_firmwareversion >= FIRMWARE_VERSION(6, 9, 0);
	default:
		return 0;
	}
}

struct dediprog_transfer_status {
	int error; /* OK if 0, ERROR else */
	unsigned int queued_idx;
	unsigned int finished_idx;
};

static void LIBUSB_CALL dediprog_bulk_read_cb(struct libusb_transfer *const transfer)
{
	struct dediprog_transfer_status *const status = (struct dediprog_transfer_status *)transfer->user_data;
	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
		status->error = 1;
		msg_perr("SPI bulk read failed!\n");
	}
	++status->finished_idx;
}

static int dediprog_bulk_read_poll(const struct dediprog_transfer_status *const status, const int finish)
{
	if (status->finished_idx >= status->queued_idx)
		return 0;

	do {
		struct timeval timeout = { 10, 0 };
		const int ret = libusb_handle_events_timeout(usb_ctx, &timeout);
		if (ret < 0) {
			msg_perr("Polling read events failed: %i %s!\n", ret, libusb_error_name(ret));
			return 1;
		}
	} while (finish && (status->finished_idx < status->queued_idx));
	return 0;
}

static int dediprog_read(enum dediprog_cmds cmd, unsigned int value, unsigned int idx, uint8_t *bytes, size_t size)
{
	return libusb_control_transfer(dediprog_handle, REQTYPE_EP_IN, cmd, value, idx,
				      (unsigned char *)bytes, size, DEFAULT_TIMEOUT);
}

static int dediprog_write(enum dediprog_cmds cmd, unsigned int value, unsigned int idx, const uint8_t *bytes, size_t size)
{
	return libusb_control_transfer(dediprog_handle, REQTYPE_EP_OUT, cmd, value, idx,
				      (unsigned char *)bytes, size, DEFAULT_TIMEOUT);
}


/* Might be useful for other USB devices as well. static for now.
 * num parameter allows user to specify one device of multiple installed */
static struct libusb_device_handle *get_device_by_vid_pid_number(uint16_t vid, uint16_t pid, unsigned int num)
{
	struct libusb_device **list;
	ssize_t count = libusb_get_device_list(usb_ctx, &list);
	if (count < 0) {
		msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
		return NULL;
	}

	struct libusb_device_handle *handle = NULL;
	ssize_t i = 0;
	for (i = 0; i < count; i++) {
		struct libusb_device *dev = list[i];
		struct libusb_device_descriptor desc;
		int err = libusb_get_device_descriptor(dev, &desc);
		if (err != 0) {
			msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(err));
			libusb_free_device_list(list, 1);
			return NULL;
		}
		if ((desc.idVendor == vid) && (desc.idProduct == pid)) {
			msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
				 desc.idVendor, desc.idProduct,
				 libusb_get_bus_number(dev), libusb_get_device_address(dev));
			if (num == 0) {
				err = libusb_open(dev, &handle);
				if (err != 0) {
					msg_perr("Opening the USB device failed (%s)!\n",
						 libusb_error_name(err));
					libusb_free_device_list(list, 1);
					return NULL;
				}
				break;
			}
			num--;
		}
	}
	libusb_free_device_list(list, 1);

	return handle;
}

/* This function sets the GPIOs connected to the LEDs as well as IO1-IO4. */
static int dediprog_set_leds(int leds)
{
	if (leds < LED_NONE || leds > LED_ALL)
		leds = LED_ALL;

	/* Older Dediprogs with 2.x.x and 3.x.x firmware only had two LEDs, assigned to different bits. So map
	 * them around if we have an old device. On those devices the LEDs map as follows:
	 *   bit 2 == 0: green light is on.
	 *   bit 0 == 0: red light is on.
	 *
	 * Additionally, the command structure has changed with the "new" protocol.
	 *
	 * FIXME: take IO pins into account
	 */
	int target_leds, ret;
	if (is_new_prot()) {
		target_leds = (leds ^ 7) << 8;
		ret = dediprog_write(CMD_SET_IO_LED, target_leds, 0, NULL, 0);
	} else {
		if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 0, 0)) {
			target_leds = ((leds & LED_ERROR) >> 2) | ((leds & LED_PASS) << 2);
		} else {
			target_leds = leds;
		}
		target_leds ^= 7;

		ret = dediprog_write(CMD_SET_IO_LED, 0x9, target_leds, NULL, 0);
	}

	if (ret != 0x0) {
		msg_perr("Command Set LED 0x%x failed (%s)!\n", leds, libusb_error_name(ret));
		return 1;
	}

	return 0;
}

static int dediprog_set_spi_voltage(int millivolt)
{
	int ret;
	uint16_t voltage_selector;

	switch (millivolt) {
	case 0:
		/* Admittedly this one is an assumption. */
		voltage_selector = 0x0;
		break;
	case 1800:
		voltage_selector = 0x12;
		break;
	case 2500:
		voltage_selector = 0x11;
		break;
	case 3500:
		voltage_selector = 0x10;
		break;
	default:
		msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
		return 1;
	}
	msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
		 millivolt % 1000);

	if (voltage_selector == 0) {
		/* Wait some time as the original driver does. */
		programmer_delay(200 * 1000);
	}
	ret = dediprog_write(CMD_SET_VCC, voltage_selector, 0, NULL, 0);
	if (ret != 0x0) {
		msg_perr("Command Set SPI Voltage 0x%x failed!\n",
			 voltage_selector);
		return 1;
	}
	if (voltage_selector != 0) {
		/* Wait some time as the original driver does. */
		programmer_delay(200 * 1000);
	}
	return 0;
}

struct dediprog_spispeeds {
	const char *const name;
	const int speed;
};

static const struct dediprog_spispeeds spispeeds[] = {
	{ "24M",	0x0 },
	{ "12M",	0x2 },
	{ "8M",		0x1 },
	{ "3M",		0x3 },
	{ "2.18M",	0x4 },
	{ "1.5M",	0x5 },
	{ "750k",	0x6 },
	{ "375k",	0x7 },
	{ NULL,		0x0 },
};

static int dediprog_set_spi_speed(unsigned int spispeed_idx)
{
	if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 0, 0)) {
		msg_pwarn("Skipping to set SPI speed because firmware is too old.\n");
		return 0;
	}

	const struct dediprog_spispeeds *spispeed = &spispeeds[spispeed_idx];
	msg_pdbg("SPI speed is %sHz\n", spispeed->name);

	int ret = dediprog_write(CMD_SET_SPI_CLK, spispeed->speed, 0, NULL, 0);
	if (ret != 0x0) {
		msg_perr("Command Set SPI Speed 0x%x failed!\n", spispeed->speed);
		return 1;
	}
	return 0;
}

static void fill_rw_cmd_payload(uint8_t *data_packet, unsigned int count, uint8_t dedi_spi_cmd, unsigned int *value, unsigned int *idx, unsigned int start) {
	/* First 5 bytes are common in both generations. */
	data_packet[0] = count & 0xff;
	data_packet[1] = (count >> 8) & 0xff;
	data_packet[2] = 0; /* RFU */
	data_packet[3] = dedi_spi_cmd; /* Read/Write Mode (currently READ_MODE_STD, WRITE_MODE_PAGE_PGM or WRITE_MODE_2B_AAI) */
	data_packet[4] = 0; /* "Opcode". Specs imply necessity only for READ_MODE_4B_ADDR_FAST and WRITE_MODE_4B_ADDR_256B_PAGE_PGM */

	if (is_new_prot()) {
		*value = *idx = 0;
		data_packet[5] = 0; /* RFU */
		data_packet[6] = (start >>  0) & 0xff;
		data_packet[7] = (start >>  8) & 0xff;
		data_packet[8] = (start >> 16) & 0xff;
		data_packet[9] = (start >> 24) & 0xff;
	} else {
		*value = start % 0x10000;
		*idx = start / 0x10000;
	}
}

/* Bulk read interface, will read multiple 512 byte chunks aligned to 512 bytes.
 * @start	start address
 * @len		length
 * @return	0 on success, 1 on failure
 */
static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
{
	int err = 1;

	/* chunksize must be 512, other sizes will NOT work at all. */
	const unsigned int chunksize = 512;
	const unsigned int count = len / chunksize;

	struct dediprog_transfer_status status = { 0, 0, 0 };
	struct libusb_transfer *transfers[DEDIPROG_ASYNC_TRANSFERS] = { NULL, };
	struct libusb_transfer *transfer;

	if ((start % chunksize) || (len % chunksize)) {
		msg_perr("%s: Unaligned start=%i, len=%i! Please report a bug at flashrom@flashrom.org\n",
			 __func__, start, len);
		return 1;
	}

	if (len == 0)
		return 0;

	/* Command packet size of protocols: new 10 B, old 5 B. */
	uint8_t data_packet[is_new_prot() ? 10 : 5];
	unsigned int value, idx;
	fill_rw_cmd_payload(data_packet, count, READ_MODE_STD, &value, &idx, start);

	int ret = dediprog_write(CMD_READ, value, idx, data_packet, sizeof(data_packet));
	if (ret != sizeof(data_packet)) {
		msg_perr("Command Read SPI Bulk failed, %i %s!\n", ret, libusb_error_name(ret));
		return 1;
	}

	/*
	 * Ring buffer of bulk transfers.
	 * Poll until at least one transfer is ready,
	 * schedule next transfers until buffer is full.
	 */

	/* Allocate bulk transfers. */
	unsigned int i;
	for (i = 0; i < min(DEDIPROG_ASYNC_TRANSFERS, count); ++i) {
		transfers[i] = libusb_alloc_transfer(0);
		if (!transfers[i]) {
			msg_perr("Allocating libusb transfer %i failed: %s!\n", i, libusb_error_name(ret));
			goto err_free;
 		}
 	}

	/* Now transfer requested chunks using libusb's asynchronous interface. */
	while (!status.error && (status.queued_idx < count)) {
		while ((status.queued_idx < count) &&
		       (status.queued_idx - status.finished_idx) < DEDIPROG_ASYNC_TRANSFERS)
		{
			transfer = transfers[status.queued_idx % DEDIPROG_ASYNC_TRANSFERS];
			libusb_fill_bulk_transfer(transfer, dediprog_handle, 0x80 | dediprog_in_endpoint,
					(unsigned char *)buf + status.queued_idx * chunksize, chunksize,
					dediprog_bulk_read_cb, &status, DEFAULT_TIMEOUT);
			transfer->flags |= LIBUSB_TRANSFER_SHORT_NOT_OK;
			ret = libusb_submit_transfer(transfer);
			if (ret < 0) {
				msg_perr("Submitting SPI bulk read %i failed: %s!\n",
					 status.queued_idx, libusb_error_name(ret));
				goto err_free;
			}
			++status.queued_idx;
		}
		if (dediprog_bulk_read_poll(&status, 0))
			goto err_free;
	}
	/* Wait for transfers to finish. */
	if (dediprog_bulk_read_poll(&status, 1))
		goto err_free;
	/* Check if everything has been transmitted. */
	if ((status.finished_idx < count) || status.error)
		goto err_free;

	err = 0;

err_free:
	dediprog_bulk_read_poll(&status, 1);
	for (i = 0; i < DEDIPROG_ASYNC_TRANSFERS; ++i)
		if (transfers[i]) libusb_free_transfer(transfers[i]);
	return err;
}

static int dediprog_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
{
	int ret;
	/* chunksize must be 512, other sizes will NOT work at all. */
	const unsigned int chunksize = 0x200;
	unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
	unsigned int bulklen;

	dediprog_set_leds(LED_BUSY);

	if (residue) {
		msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n",
			 start, residue);
		ret = spi_read_chunked(flash, buf, start, residue, 16);
		if (ret)
			goto err;
	}

	/* Round down. */
	bulklen = (len - residue) / chunksize * chunksize;
	ret = dediprog_spi_bulk_read(flash, buf + residue, start + residue, bulklen);
	if (ret)
		goto err;

	len -= residue + bulklen;
	if (len != 0) {
		msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n",
			 start, len);
		ret = spi_read_chunked(flash, buf + residue + bulklen,
				       start + residue + bulklen, len, 16);
		if (ret)
			goto err;
	}

	dediprog_set_leds(LED_PASS);
	return 0;
err:
	dediprog_set_leds(LED_ERROR);
	return ret;
}

/* Bulk write interface, will write multiple chunksize byte chunks aligned to chunksize bytes.
 * @chunksize       length of data chunks, only 256 supported by now
 * @start           start address
 * @len             length
 * @dedi_spi_cmd    dediprog specific write command for spi bus
 * @return          0 on success, 1 on failure
 */
static int dediprog_spi_bulk_write(struct flashctx *flash, const uint8_t *buf, unsigned int chunksize,
				   unsigned int start, unsigned int len, uint8_t dedi_spi_cmd)
{
	/* USB transfer size must be 512, other sizes will NOT work at all.
	 * chunksize is the real data size per USB bulk transfer. The remaining
	 * space in a USB bulk transfer must be filled with 0xff padding.
	 */
	const unsigned int count = len / chunksize;

	/*
	 * We should change this check to
	 *   chunksize > 512
	 * once we know how to handle different chunk sizes.
	 */
	if (chunksize != 256) {
		msg_perr("%s: Chunk sizes other than 256 bytes are unsupported, chunksize=%u!\n"
			 "Please report a bug at flashrom@flashrom.org\n", __func__, chunksize);
		return 1;
	}

	if ((start % chunksize) || (len % chunksize)) {
		msg_perr("%s: Unaligned start=%i, len=%i! Please report a bug "
			 "at flashrom@flashrom.org\n", __func__, start, len);
		return 1;
	}

	/* No idea if the hardware can handle empty writes, so chicken out. */
	if (len == 0)
		return 0;

	/* Command packet size of protocols: new 10 B, old 5 B. */
	uint8_t data_packet[is_new_prot() ? 10 : 5];
	unsigned int value, idx;
	fill_rw_cmd_payload(data_packet, count, dedi_spi_cmd, &value, &idx, start);
	int ret = dediprog_write(CMD_WRITE, value, idx, data_packet, sizeof(data_packet));
	if (ret != sizeof(data_packet)) {
		msg_perr("Command Write SPI Bulk failed, %s!\n", libusb_error_name(ret));
		return 1;
	}

	unsigned int i;
	for (i = 0; i < count; i++) {
		unsigned char usbbuf[512];
		memcpy(usbbuf, buf + i * chunksize, chunksize);
		memset(usbbuf + chunksize, 0xff, sizeof(usbbuf) - chunksize); // fill up with 0xFF
		int transferred;
		ret = libusb_bulk_transfer(dediprog_handle, dediprog_out_endpoint, usbbuf, 512, &transferred,
					   DEFAULT_TIMEOUT);
		if ((ret < 0) || (transferred != 512)) {
			msg_perr("SPI bulk write failed, expected %i, got %s!\n", 512, libusb_error_name(ret));
			return 1;
		}
	}

	return 0;
}

static int dediprog_spi_write(struct flashctx *flash, const uint8_t *buf,
			      unsigned int start, unsigned int len, uint8_t dedi_spi_cmd)
{
	int ret;
	const unsigned int chunksize = flash->chip->page_size;
	unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
	unsigned int bulklen;

	dediprog_set_leds(LED_BUSY);

	if (chunksize != 256) {
		msg_pdbg("Page sizes other than 256 bytes are unsupported as "
			 "we don't know how dediprog\nhandles them.\n");
		/* Write everything like it was residue. */
		residue = len;
	}

	if (residue) {
		msg_pdbg("Slow write for partial block from 0x%x, length 0x%x\n",
			 start, residue);
		/* No idea about the real limit. Maybe 12, maybe more. */
		ret = spi_write_chunked(flash, buf, start, residue, 12);
		if (ret) {
			dediprog_set_leds(LED_ERROR);
			return ret;
		}
	}

	/* Round down. */
	bulklen = (len - residue) / chunksize * chunksize;
	ret = dediprog_spi_bulk_write(flash, buf + residue, chunksize, start + residue, bulklen, dedi_spi_cmd);
	if (ret) {
		dediprog_set_leds(LED_ERROR);
		return ret;
	}

	len -= residue + bulklen;
	if (len) {
		msg_pdbg("Slow write for partial block from 0x%x, length 0x%x\n",
			 start, len);
		ret = spi_write_chunked(flash, buf + residue + bulklen,
				        start + residue + bulklen, len, 12);
		if (ret) {
			dediprog_set_leds(LED_ERROR);
			return ret;
		}
	}

	dediprog_set_leds(LED_PASS);
	return 0;
}

static int dediprog_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
{
	return dediprog_spi_write(flash, buf, start, len, WRITE_MODE_PAGE_PGM);
}

static int dediprog_spi_write_aai(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
{
	return dediprog_spi_write(flash, buf, start, len, WRITE_MODE_2B_AAI);
}

static int dediprog_spi_send_command(struct flashctx *flash,
				     unsigned int writecnt,
				     unsigned int readcnt,
				     const unsigned char *writearr,
				     unsigned char *readarr)
{
	int ret;

	msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt);
	if (writecnt > flash->mst->spi.max_data_write) {
		msg_perr("Invalid writecnt=%i, aborting.\n", writecnt);
		return 1;
	}
	if (readcnt > flash->mst->spi.max_data_read) {
		msg_perr("Invalid readcnt=%i, aborting.\n", readcnt);
		return 1;
	}
	
	unsigned int idx, value;
	/* New protocol has options and timeout combined as value while the old one used the value field for
	 * timeout and the index field for options. */
	if (is_new_prot()) {
		idx = 0;
		value = readcnt ? 0x1 : 0x0; // Indicate if we require a read
	} else {
		idx = readcnt ? 0x1 : 0x0; // Indicate if we require a read
		value = 0;
	}
	ret = dediprog_write(CMD_TRANSCEIVE, value, idx, writearr, writecnt);
	if (ret != writecnt) {
		msg_perr("Send SPI failed, expected %i, got %i %s!\n",
			 writecnt, ret, libusb_error_name(ret));
		return 1;
	}
	if (readcnt == 0) // If we don't require a response, we are done here
		return 0;

	/* The specifications do state the possibility to set a timeout for transceive transactions.
	 * Apparently the "timeout" is a delay, and you can use long delays to accelerate writing - in case you
	 * can predict the time needed by the previous command or so (untested). In any case, using this
	 * "feature" to set sane-looking timouts for the read below will completely trash performance with
	 * SF600 and/or firmwares >= 6.0 while they seem to be benign on SF100 with firmwares <= 5.5.2. *shrug*
	 *
	 * The specification also uses only 0 in its examples, so the lesson to learn here:
	 * "Never trust the description of an interface in the documentation but use the example code and pray."
	const uint8_t read_timeout = 10 + readcnt/512;
	if (is_new_prot()) {
		idx = 0;
		value = min(read_timeout, 0xFF) | (0 << 8) ; // Timeout in lower byte, option in upper byte
	} else {
		idx = (0 & 0xFF);  // Lower byte is option (0x01 = require SR, 0x02 keep CS low)
		value = min(read_timeout, 0xFF); // Possibly two bytes but we play safe here
	}
	ret = dediprog_read(CMD_TRANSCEIVE, value, idx, readarr, readcnt);
	*/
	ret = dediprog_read(CMD_TRANSCEIVE, 0, 0, readarr, readcnt);
	if (ret != readcnt) {
		msg_perr("Receive SPI failed, expected %i, got %i %s!\n", readcnt, ret, libusb_error_name(ret));
		return 1;
	}
	return 0;
}

static int dediprog_check_devicestring(void)
{
	int ret;
	char buf[0x11];

	/* Command Receive Device String. */
	ret = dediprog_read(CMD_READ_PROG_INFO, 0, 0, (uint8_t *)buf, 0x10);
	if (ret != 0x10) {
		msg_perr("Incomplete/failed Command Receive Device String!\n");
		return 1;
	}
	buf[0x10] = '\0';
	msg_pdbg("Found a %s\n", buf);
	if (memcmp(buf, "SF100", 0x5) == 0)
		dediprog_devicetype = DEV_SF100;
	else if (memcmp(buf, "SF600", 0x5) == 0)
		dediprog_devicetype = DEV_SF600;
	else {
		msg_perr("Device not a SF100 or SF600!\n");
		return 1;
	}

	int sfnum;
	int fw[3];
	if (sscanf(buf, "SF%d V:%d.%d.%d ", &sfnum, &fw[0], &fw[1], &fw[2]) != 4 ||
	    sfnum != dediprog_devicetype) {
		msg_perr("Unexpected firmware version string '%s'\n", buf);
		return 1;
	}
	/* Only these major versions were tested. */
	if (fw[0] < 2 || fw[0] > 7) {
		msg_perr("Unexpected firmware version %d.%d.%d!\n", fw[0], fw[1], fw[2]);
		return 1;
	}
	dediprog_firmwareversion = FIRMWARE_VERSION(fw[0], fw[1], fw[2]);

	return 0;
}

/*
 * This command presumably sets the voltage for the SF100 itself (not the
 * SPI flash). Only use this command with firmware older than V6.0.0. Newer
 * (including all SF600s) do not support it.
 */

/* This command presumably sets the voltage for the SF100 itself (not the SPI flash).
 * Only use dediprog_set_voltage on SF100 programmers with firmware older
 * than V6.0.0. Newer programmers (including all SF600s) do not support it. */
static int dediprog_set_voltage(void)
{
	unsigned char buf[1] = {0};
	int ret = libusb_control_transfer(dediprog_handle, REQTYPE_OTHER_IN, CMD_SET_VOLTAGE, 0x0, 0x0,
			      buf, 0x1, DEFAULT_TIMEOUT);
	if (ret < 0) {
		msg_perr("Command Set Voltage failed (%s)!\n", libusb_error_name(ret));
		return 1;
	}
	if ((ret != 1) || (buf[0] != 0x6f)) {
		msg_perr("Unexpected response to init!\n");
		return 1;
	}

	return 0;
}

static int dediprog_standalone_mode(void)
{
	int ret;

	if (dediprog_devicetype != DEV_SF600)
		return 0;

	msg_pdbg2("Disabling standalone mode.\n");
	ret = dediprog_write(CMD_SET_STANDALONE, LEAVE_STANDALONE_MODE, 0, NULL, 0);
	if (ret) {
		msg_perr("Failed to disable standalone mode: %s\n", libusb_error_name(ret));
		return 1;
	}

	return 0;
}

#if 0
/* Something.
 * Present in eng_detect_blink.log with firmware 3.1.8
 * Always preceded by Command Receive Device String
 */
static int dediprog_command_b(void)
{
	int ret;
	char buf[0x3];

	ret = usb_control_msg(dediprog_handle, REQTYPE_OTHER_IN, 0x7, 0x0, 0xef00,
			      buf, 0x3, DEFAULT_TIMEOUT);
	if (ret < 0) {
		msg_perr("Command B failed (%s)!\n", libusb_error_name(ret));
		return 1;
	}
	if ((ret != 0x3) || (buf[0] != 0xff) || (buf[1] != 0xff) ||
	    (buf[2] != 0xff)) {
		msg_perr("Unexpected response to Command B!\n");
		return 1;
	}

	return 0;
}
#endif

static int set_target_flash(enum dediprog_target target)
{
	int ret = dediprog_write(CMD_SET_TARGET, target, 0, NULL, 0);
	if (ret != 0) {
		msg_perr("set_target_flash failed (%s)!\n", libusb_error_name(ret));
		return 1;
	}
	return 0;
}

#if 0
/* Returns true if the button is currently pressed. */
static bool dediprog_get_button(void)
{
	char buf[1];
	int ret = usb_control_msg(dediprog_handle, REQTYPE_EP_IN, CMD_GET_BUTTON, 0, 0,
			      buf, 0x1, DEFAULT_TIMEOUT);
	if (ret != 0) {
		msg_perr("Could not get button state (%s)!\n", libusb_error_name(ret));
		return 1;
	}
	return buf[0] != 1;
}
#endif

static int parse_voltage(char *voltage)
{
	char *tmp = NULL;
	int i;
	int millivolt = 0, fraction = 0;

	if (!voltage || !strlen(voltage)) {
		msg_perr("Empty voltage= specified.\n");
		return -1;
	}
	millivolt = (int)strtol(voltage, &tmp, 0);
	voltage = tmp;
	/* Handle "," and "." as decimal point. Everything after it is assumed
	 * to be in decimal notation.
	 */
	if ((*voltage == '.') || (*voltage == ',')) {
		voltage++;
		for (i = 0; i < 3; i++) {
			fraction *= 10;
			/* Don't advance if the current character is invalid,
			 * but continue multiplying.
			 */
			if ((*voltage < '0') || (*voltage > '9'))
				continue;
			fraction += *voltage - '0';
			voltage++;
		}
		/* Throw away remaining digits. */
		voltage += strspn(voltage, "0123456789");
	}
	/* The remaining string must be empty or "mV" or "V". */
	tolower_string(voltage);

	/* No unit or "V". */
	if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
		millivolt *= 1000;
		millivolt += fraction;
	} else if (!strncmp(voltage, "mv", 2) ||
		   !strncmp(voltage, "milliv", 6)) {
		/* No adjustment. fraction is discarded. */
	} else {
		/* Garbage at the end of the string. */
		msg_perr("Garbage voltage= specified.\n");
		return -1;
	}
	return millivolt;
}

static const struct spi_master spi_master_dediprog = {
	.type		= SPI_CONTROLLER_DEDIPROG,
	.max_data_read	= 16, /* 18 seems to work fine as well, but 19 times out sometimes with FW 5.15. */
	.max_data_write	= 16,
	.command	= dediprog_spi_send_command,
	.multicommand	= default_spi_send_multicommand,
	.read		= dediprog_spi_read,
	.write_256	= dediprog_spi_write_256,
	.write_aai	= dediprog_spi_write_aai,
};

static int dediprog_shutdown(void *data)
{
	dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
	dediprog_devicetype = DEV_UNKNOWN;

	/* URB 28. Command Set SPI Voltage to 0. */
	if (dediprog_set_spi_voltage(0x0))
		return 1;

	if (libusb_release_interface(dediprog_handle, 0)) {
		msg_perr("Could not release USB interface!\n");
		return 1;
	}
	libusb_close(dediprog_handle);
	libusb_exit(usb_ctx);

	return 0;
}

int dediprog_init(void)
{
	char *voltage, *device, *spispeed, *target_str;
	int spispeed_idx = 1;
	int millivolt = 3500;
	long usedevice = 0;
	long target = FLASH_TYPE_APPLICATION_FLASH_1;
	int i, ret;

	spispeed = extract_programmer_param("spispeed");
	if (spispeed) {
		for (i = 0; spispeeds[i].name; ++i) {
			if (!strcasecmp(spispeeds[i].name, spispeed)) {
				spispeed_idx = i;
				break;
			}
		}
		if (!spispeeds[i].name) {
			msg_perr("Error: Invalid spispeed value: '%s'.\n", spispeed);
			free(spispeed);
			return 1;
		}
		free(spispeed);
	}

	voltage = extract_programmer_param("voltage");
	if (voltage) {
		millivolt = parse_voltage(voltage);
		free(voltage);
		if (millivolt < 0)
			return 1;
		msg_pinfo("Setting voltage to %i mV\n", millivolt);
	}

	device = extract_programmer_param("device");
	if (device) {
		char *dev_suffix;
		errno = 0;
		usedevice = strtol(device, &dev_suffix, 10);
		if (errno != 0 || device == dev_suffix) {
			msg_perr("Error: Could not convert 'device'.\n");
			free(device);
			return 1;
		}
		if (usedevice < 0 || usedevice > UINT_MAX) {
			msg_perr("Error: Value for 'device' is out of range.\n");
			free(device);
			return 1;
		}
		if (strlen(dev_suffix) > 0) {
			msg_perr("Error: Garbage following 'device' value.\n");
			free(device);
			return 1;
		}
		msg_pinfo("Using device %li.\n", usedevice);
	}
	free(device);

	target_str = extract_programmer_param("target");
	if (target_str) {
		char *target_suffix;
		errno = 0;
		target = strtol(target_str, &target_suffix, 10);
		if (errno != 0 || target_str == target_suffix) {
			msg_perr("Error: Could not convert 'target'.\n");
			free(target_str);
			return 1;
		}
		if (target < 1 || target > 2) {
			msg_perr("Error: Value for 'target' is out of range.\n");
			free(target_str);
			return 1;
		}
		if (strlen(target_suffix) > 0) {
			msg_perr("Error: Garbage following 'target' value.\n");
			free(target_str);
			return 1;
		}
		switch (target) {
		case 1:
			msg_pinfo("Using target %s.\n", "FLASH_TYPE_APPLICATION_FLASH_1");
			target = FLASH_TYPE_APPLICATION_FLASH_1;
			break;
		case 2:
			msg_pinfo("Using target %s.\n", "FLASH_TYPE_APPLICATION_FLASH_2");
			target = FLASH_TYPE_APPLICATION_FLASH_2;
			break;
		default:
			break;
		}
	}
	free(target_str);

	/* Here comes the USB stuff. */
	libusb_init(&usb_ctx);
	if (!usb_ctx) {
		msg_perr("Could not initialize libusb!\n");
		return 1;
	}

	const uint16_t vid = devs_dediprog[0].vendor_id;
	const uint16_t pid = devs_dediprog[0].device_id;
	dediprog_handle = get_device_by_vid_pid_number(vid, pid, (unsigned int) usedevice);
	if (!dediprog_handle) {
		msg_perr("Could not find a Dediprog programmer on USB.\n");
		libusb_exit(usb_ctx);
		return 1;
	}
	ret = libusb_set_configuration(dediprog_handle, 1);
	if (ret != 0) {
		msg_perr("Could not set USB device configuration: %i %s\n", ret, libusb_error_name(ret));
		libusb_close(dediprog_handle);
		libusb_exit(usb_ctx);
		return 1;
	}
	ret = libusb_claim_interface(dediprog_handle, 0);
	if (ret < 0) {
		msg_perr("Could not claim USB device interface %i: %i %s\n", 0, ret, libusb_error_name(ret));
		libusb_close(dediprog_handle);
		libusb_exit(usb_ctx);
		return 1;
	}

	if (register_shutdown(dediprog_shutdown, NULL))
		return 1;

	/* Try reading the devicestring. If that fails and the device is old (FW < 6.0.0, which we can not know)
	 * then we need to try the "set voltage" command and then attempt to read the devicestring again. */
	if (dediprog_check_devicestring()) {
		if (dediprog_set_voltage())
			return 1;
		if (dediprog_check_devicestring())
			return 1;
	}

	/* SF100 only has 1 endpoint for in/out, SF600 uses two separate endpoints instead. */
	dediprog_in_endpoint = 2;
	if (dediprog_devicetype == DEV_SF100)
		dediprog_out_endpoint = 2;
	else
		dediprog_out_endpoint = 1;

	/* Set all possible LEDs as soon as possible to indicate activity.
	 * Because knowing the firmware version is required to set the LEDs correctly we need to this after
	 * dediprog_check_devicestring() has queried the device and set dediprog_firmwareversion. */
	dediprog_set_leds(LED_ALL);

	/* Select target/socket, frequency and VCC. */
	if (set_target_flash(target) ||
	    dediprog_set_spi_speed(spispeed_idx) ||
	    dediprog_set_spi_voltage(millivolt)) {
		dediprog_set_leds(LED_ERROR);
		return 1;
	}

	if (dediprog_standalone_mode())
		return 1;

	if (register_spi_master(&spi_master_dediprog) || dediprog_set_leds(LED_NONE))
		return 1;

	return 0;
}