Forked from
PureOS / Core Packages / flashrom
21 commits behind, 47 commits ahead of the upstream repository.
-
Edward O'Callaghan authored
The ChromiumOS flashrom fork has since const'ify flashctx in a few places. This aligns the function signatures to match with downstream to ease forward porting patches out of downstream back into mainline flashrom. This patch is minimum viable alignment and so feedback is welcome. Change-Id: Iff6dbda13cb0d941481c0d204b9c30895630fbd1 Signed-off-by:
Edward O'Callaghan <quasisec@google.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/40324 Reviewed-by:
Angel Pons <th3fanbus@gmail.com> Tested-by:
build bot (Jenkins) <no-reply@coreboot.org>
Edward O'Callaghan authoredThe ChromiumOS flashrom fork has since const'ify flashctx in a few places. This aligns the function signatures to match with downstream to ease forward porting patches out of downstream back into mainline flashrom. This patch is minimum viable alignment and so feedback is welcome. Change-Id: Iff6dbda13cb0d941481c0d204b9c30895630fbd1 Signed-off-by:
Edward O'Callaghan <quasisec@google.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/40324 Reviewed-by:
Angel Pons <th3fanbus@gmail.com> Tested-by:
build bot (Jenkins) <no-reply@coreboot.org>
dediprog.c 35.79 KiB
/*
* 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.
*/
#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 */
static 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_SF200 = 200,
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,
};
/*
* These are not official designations; they are for use in flashrom only.
* Order must be preserved so that comparison operators work.
*/
enum protocol {
PROTOCOL_UNKNOWN,
PROTOCOL_V1,
PROTOCOL_V2,
PROTOCOL_V3,
};
const struct dev_entry devs_dediprog[] = {
{0x0483, 0xDADA, OK, "Dediprog", "SF100/SF200/SF600"},
{0},
};
static int dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
static 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
static enum protocol protocol(void)
{
/* Firmware version < 5.0.0 is handled explicitly in some cases. */
switch (dediprog_devicetype) {
case DEV_SF100:
case DEV_SF200:
if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 5, 0))
return PROTOCOL_V1;
else
return PROTOCOL_V2;
case DEV_SF600:
if (dediprog_firmwareversion < FIRMWARE_VERSION(6, 9, 0))
return PROTOCOL_V1;
else if (dediprog_firmwareversion <= FIRMWARE_VERSION(7, 2, 21))
return PROTOCOL_V2;
else
return PROTOCOL_V3;
default:
return PROTOCOL_UNKNOWN;
}
}
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);
}
/* 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 (protocol() >= PROTOCOL_V2) {
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 int prepare_rw_cmd(
struct flashctx *const flash, uint8_t *data_packet, unsigned int count,
uint8_t dedi_spi_cmd, unsigned int *value, unsigned int *idx, unsigned int start, int is_read)
{
if (count >= 1 << 16) {
msg_perr("%s: Unsupported transfer length of %u blocks! "
"Please report a bug at flashrom@flashrom.org\n",
__func__, count);
return 1;
}
/* 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 (protocol() >= PROTOCOL_V2) {
if (is_read && flash->chip->feature_bits & FEATURE_4BA_FAST_READ) {
data_packet[3] = READ_MODE_4B_ADDR_FAST_0x0C;
data_packet[4] = JEDEC_READ_4BA_FAST;
} else if (dedi_spi_cmd == WRITE_MODE_PAGE_PGM
&& (flash->chip->feature_bits & FEATURE_4BA_WRITE)) {
data_packet[3] = WRITE_MODE_4B_ADDR_256B_PAGE_PGM_0x12;
data_packet[4] = JEDEC_BYTE_PROGRAM_4BA;
}
*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;
if (protocol() >= PROTOCOL_V3) {
if (is_read) {
data_packet[10] = 0x00; /* address length (3 or 4) */
data_packet[11] = 0x00; /* dummy cycle / 2 */
} else {
/* 16 LSBs and 16 HSBs of page size */
/* FIXME: This assumes page size of 256. */
data_packet[10] = 0x00;
data_packet[11] = 0x01;
data_packet[12] = 0x00;
data_packet[13] = 0x00;
}
}
} else {
if (flash->chip->feature_bits & FEATURE_4BA_EXT_ADDR) {
if (spi_set_extended_address(flash, start >> 24))
return 1;
} else if (start >> 24) {
msg_cerr("Can't handle 4-byte address with dediprog.\n");
return 1;
}
/*
* We don't know how the dediprog firmware handles 4-byte
* addresses. So let's not tell it what we are doing and
* only send the lower 3 bytes.
*/
*value = start & 0xffff;
*idx = (start >> 16) & 0xff;
}
return 0;
}
/* 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 (len == 0)
return 0;
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;
}
int command_packet_size;
switch (protocol()) {
case PROTOCOL_V1:
command_packet_size = 5;
break;
case PROTOCOL_V2:
command_packet_size = 10;
break;
case PROTOCOL_V3:
command_packet_size = 12;
break;
default:
return 1;
}
uint8_t data_packet[command_packet_size];
unsigned int value, idx;
if (prepare_rw_cmd(flash, data_packet, count, READ_MODE_STD, &value, &idx, start, 1))
return 1;
int ret = dediprog_write(CMD_READ, value, idx, data_packet, sizeof(data_packet));
if (ret != (int)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 ? min(len, 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;
int command_packet_size;
switch (protocol()) {
case PROTOCOL_V1:
command_packet_size = 5;
break;
case PROTOCOL_V2:
command_packet_size = 10;
break;
case PROTOCOL_V3:
command_packet_size = 14;
break;
default:
return 1;
}
uint8_t data_packet[command_packet_size];
unsigned int value, idx;
if (prepare_rw_cmd(flash, data_packet, count, dedi_spi_cmd, &value, &idx, start, 0))
return 1;
int ret = dediprog_write(CMD_WRITE, value, idx, data_packet, sizeof(data_packet));
if (ret != (int)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 16 including command and address, maybe more. */
ret = spi_write_chunked(flash, buf, start, residue, 11);
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, 11);
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(const 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 (protocol() >= PROTOCOL_V2) {
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 != (int)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 (protocol() >= PROTOCOL_V2) {
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 != (int)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, "SF200", 0x5) == 0)
dediprog_devicetype = DEV_SF200;
else if (memcmp(buf, "SF600", 0x5) == 0)
dediprog_devicetype = DEV_SF600;
else {
msg_perr("Device not a SF100, SF200, 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 != (int)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]);
if (protocol() == PROTOCOL_UNKNOWN) {
msg_perr("Internal error: Unable to determine protocol version.\n");
return 1;
}
return 0;
}
/*
* Read the id from the dediprog. This should return the numeric part of the
* serial number found on a sticker on the back of the dediprog. Note this
* number is stored in writable eeprom, so it could get out of sync. Also note,
* this function only supports SF100 at this time, but SF600 support is not too
* much different.
* @return the id on success, -1 on failure
*/
static int dediprog_read_id(void)
{
int ret;
uint8_t buf[3];
ret = libusb_control_transfer(dediprog_handle, REQTYPE_OTHER_IN,
0x7, /* request */
0, /* value */
0xEF00, /* index */
buf, sizeof(buf),
DEFAULT_TIMEOUT);
if (ret != sizeof(buf)) {
msg_perr("Failed to read dediprog id, error %d!\n", ret);
return -1;
}
return buf[0] << 16 | buf[1] << 8 | buf[2];
}
/*
* 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 struct spi_master spi_master_dediprog = {
.features = SPI_MASTER_NO_4BA_MODES,
.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,
};
/*
* Open a dediprog_handle with the USB device at the given index.
* @index index of the USB device
* @return 0 for success, -1 for error, -2 for busy device
*/
static int dediprog_open(int index)
{
const uint16_t vid = devs_dediprog[0].vendor_id;
const uint16_t pid = devs_dediprog[0].device_id;
int ret;
dediprog_handle = usb_dev_get_by_vid_pid_number(usb_ctx, vid, pid, (unsigned int) index);
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);
return -2;
}
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);
return -2;
}
return 0;
}
static int dediprog_shutdown(void *data)
{
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, *id_str, *device, *spispeed, *target_str;
int spispeed_idx = 1;
int millivolt = 3500;
int id = -1; /* -1 defaults to enumeration order */
int found_id;
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);
}
id_str = extract_programmer_param("id");
if (id_str) {
char prefix0, prefix1;
if (sscanf(id_str, "%c%c%d", &prefix0, &prefix1, &id) != 3) {
msg_perr("Error: Could not parse dediprog 'id'.\n");
msg_perr("Expected a string like SF012345 or DP012345.\n");
free(id_str);
return 1;
}
if (id < 0 || id >= 0x1000000) {
msg_perr("Error: id %s is out of range!\n", id_str);
free(id_str);
return 1;
}
if (!(prefix0 == 'S' && prefix1 == 'F') && !(prefix0 == 'D' && prefix1 == 'P')) {
msg_perr("Error: %s is an invalid id!\n", id_str);
free(id_str);
return 1;
}
msg_pinfo("Will search for dediprog id %s.\n", id_str);
}
free(id_str);
device = extract_programmer_param("device");
if (device) {
char *dev_suffix;
if (id != -1) {
msg_perr("Error: Cannot use 'id' and 'device'.\n");
}
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 > INT_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;
}
if (id != -1) {
for (i = 0; ; i++) {
ret = dediprog_open(i);
if (ret == -1) {
/* no dev */
libusb_exit(usb_ctx);
return 1;
} else if (ret == -2) {
/* busy dev */
continue;
}
/* Notice we can only call dediprog_read_id() after
* libusb_set_configuration() and
* libusb_claim_interface(). When searching by id and
* either configuration or claim fails (usually the
* device is in use by another instance of flashrom),
* the device is skipped and the next device is tried.
*/
found_id = dediprog_read_id();
if (found_id < 0) {
msg_perr("Could not read id.\n");
libusb_release_interface(dediprog_handle, 0);
libusb_close(dediprog_handle);
continue;
}
msg_pinfo("Found dediprog id SF%06d.\n", found_id);
if (found_id != id) {
libusb_release_interface(dediprog_handle, 0);
libusb_close(dediprog_handle);
continue;
}
break;
}
} else {
if (dediprog_open(usedevice)) {
return 1;
}
found_id = dediprog_read_id();
}
if (found_id >= 0) {
msg_pinfo("Using dediprog id SF%06d.\n", found_id);
}
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/SF200 uses one in/out endpoint, SF600 uses separate in/out endpoints */
dediprog_in_endpoint = 2;
switch (dediprog_devicetype) {
case DEV_SF100:
case DEV_SF200:
dediprog_out_endpoint = 2;
break;
default:
dediprog_out_endpoint = 1;
break;
}
/* 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. */
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 (dediprog_devicetype == DEV_SF100 && protocol() == PROTOCOL_V1)
spi_master_dediprog.features &= ~SPI_MASTER_NO_4BA_MODES;
if (protocol() == PROTOCOL_V2)
spi_master_dediprog.features |= SPI_MASTER_4BA;
if (register_spi_master(&spi_master_dediprog) || dediprog_set_leds(LED_NONE))
return 1;
return 0;
}