Just for fun, here's the whole tool (I think it has no dependencies):
/*
Copyright (C) 2010 DJ Delorie <dj@xxxxxxxxxx>
This file 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, or (at your option) any later
version.
This file 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 file; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>.
*/
/* This is the start of a program to read and write the EEPROM in the
FT232R family of chips. It currently works partly; it doesn't set
all the fields that the FTDI utility does. Mostly, this exists for
non-Windows platforms. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <getopt.h>
#include <ftdi.h>
typedef unsigned char byte;
extern int verbose;
#define dprintf if (verbose>1) printf
static struct ftdi_context handle_s;
static struct ftdi_context *handle = &handle_s;
#define MAX_EEPROM 256
int read_mode = 0;
int write_mode = 0;
const char *ept_filename;
FILE *ept_file;
char *read_file = NULL;
char *write_file = NULL;
typedef struct {
byte b0; /* 00 */
#define EE_B0_HIGH_CURRENT 0x04
#define EE_B0_D2XX_DRIVER 0x08
byte b1; /* 01 */
byte idVendor[2]; /* 02-03 */
byte idProduct[2]; /* 04-05 */
byte device[2]; /* 06-07 */
byte config; /* 08 */
#define EE_CONFIG_BASE 0x80
#define EE_CONFIG_SELFPOWER 0x40
#define EE_CONFIG_REMOTEWAKE 0x20
#define EE_CONFIG_BATTERYPOWER 0x10
byte max_power; /* 09 */
byte chip_config; /* 0A */
#define EE_CHIP_USB_VERSION 0x10
#define EE_CHIP_SERIALNO 0x08
#define EE_CHIP_SUSPEND_PD 0x04
#define EE_CHIP_OUT_ISOCHRON 0x02
#define EE_CHIP_IN_ISOCHRON 0x01
byte invert; /* 0B */
#define EE_INVERT_RI 0x80
#define EE_INVERT_DCD 0x40
#define EE_INVERT_DSR 0x20
#define EE_INVERT_DTR 0x10
#define EE_INVERT_CTS 0x08
#define EE_INVERT_RTS 0x04
#define EE_INVERT_RXD 0x02
#define EE_INVERT_TXD 0x01
byte usb_version_low; /* 0C */
byte usb_version_high; /* 0D */
byte manuf_str_ofs; /* 0E */
byte manuf_str_len; /* 0F */
byte device_str_ofs; /* 10 */
byte device_str_len; /* 11 */
byte serial_str_ofs; /* 12 */
byte serial_str_len; /* 13 */
byte c0_c1; /* 14 */
byte c2_c3; /* 15 */
byte c4; /* 16 */
byte strings[0x7e - 0x17]; /* 17 - 7D */
#define STRING_BASE 0x17;
byte checksum_low; /* 7E */
byte checksum_high; /* 7F */
} EEProm;
typedef struct {
union {
byte buf[MAX_EEPROM];
EEProm eeprom;
};
int buf_size;
char manuf[MAX_EEPROM];
char device[MAX_EEPROM];
char serial[MAX_EEPROM];
} EEPROM;
EEPROM rbuf;
EEPROM wbuf;
static int
Fail1(int status, const char *file, int line, const char *sline)
{
const char *er = "Unknown error";
if (status >= 0)
return status;
er = ftdi_get_error_string (handle);
printf("%s:%d: Error %s (%d) doing %s\n", file, line, er, status, sline);
exit(1);
}
#define Fail(x) Fail1(x,__FILE__,__LINE__,#x)
/* ------------------------------------------------------------ */
#ifdef __WIN32
#undef isatty
#define isatty(x) 0
#endif
static void
color (int n)
{
static int tty = -2;
static int last_color = -2;
if (tty == -2)
tty = isatty (1);
if (!tty)
return;
if (n == last_color)
return;
last_color = n;
printf("\033[%dm", n);
}
static int
min3 (int a, int b, int c)
{
if (a < b && a < c)
return a;
if (b < c)
return b;
return c;
}
static const char *cbus_purposes[16] = {
"txden", "pwron", "rxled", "txled",
"txrx", "sleep", "clk48", "clk24",
"clk12", "clk6", "io", "bbWR",
"bbRD", "rxf", "?14", "?15"
};
#define CBUS_TXDEN 0
#define CBUS_PWRON 1
#define CBUS_RXLED 2
#define CBUS_TXLED 3
#define CBUS_TXRX 4
#define CBUS_SLEEP 5
#define CBUS_CLK48 6
#define CBUS_CLK24 7
#define CBUS_CLK12 8
#define CBUS_CLK6 9
#define CBUS_IO 10
#define CBUS_BBWR 11
#define CBUS_BBRD 12
#define CBUS_RXF 13
static const char *inverts[8] = {
"txd", "rxd", "rts", "cts", "dtr", "dsr", "dcd", "ri"
};
static char *cb, *ci, *cs, *cr;
static void
cbus (int which, int purpose)
{
printf("CBUS%d: %s%s%s", which, cb, cbus_purposes[purpose], cr);
if (which == 4)
printf("\n");
else
printf("\t");
}
static void
decode_imagebuf (EEPROM *eeprom)
{
EEProm *e = & eeprom->eeprom;
int i;
unsigned short ck;
if (isatty (1))
{
cb = "\033[34m";
ci = "\033[36m";
cs = "\033[32m";
cr = "\033[0m";
}
else
{
cb = ci = cs = cr = "";
}
printf("High Current: %s%s%s\n", cb, (e->b0 & EE_B0_HIGH_CURRENT) ? "enabled"
: "disabled", cr);
printf("Driver: %s%s%s\n", cb, (e->b0 & EE_B0_D2XX_DRIVER) ? "D2XX" : "VCP",
cr);
printf("idVendor: %s0x%02x%02x%s\t", ci, e->idVendor[1], e->idVendor[0], cr);
printf("idProduct: %s0x%02x%02x%s\t", ci, e->idProduct[1], e->idProduct[0],
cr);
printf("idDevice: %s0x%02x%02x%s\n", ci, e->device[1], e->device[0], cr);
printf("config:%s", cb);
if (e->config & EE_CONFIG_SELFPOWER) printf(" self-powered");
if (e->config & EE_CONFIG_REMOTEWAKE) printf(" remote-wake");
if (e->config & EE_CONFIG_BATTERYPOWER) printf(" battery-power");
printf("%s\n", cr);
printf("Max power: %s%d%s mA\n", ci, e->max_power * 2, cr);
printf("Chip config:%s", cb);
if (e->chip_config & EE_CHIP_USB_VERSION) printf(" use-usb-version");
if (e->chip_config & EE_CHIP_SERIALNO) printf(" use-serial-no");
if (e->chip_config & EE_CHIP_SUSPEND_PD) printf(" suspend-pull-down");
if (e->chip_config & EE_CHIP_OUT_ISOCHRON) printf(" out-isochronos");
if (e->chip_config & EE_CHIP_IN_ISOCHRON) printf(" in-isochronos");
printf("%s\n", cr);
printf("Invert:%s", cb);
for (i=0; i<8; i++)
if (e->invert & (1 << i)) printf(" %s", inverts[i]);
printf("%s\n", cr);
printf("USB Version: %s%d.%d%s\n", ci, e->usb_version_high,
e->usb_version_low, cr);
printf ("Manufacturer: \"%s%s%s\"\n", cs, eeprom->manuf, cr);
printf ("Device: \"%s%s%s\"\n", cs, eeprom->device, cr);
printf ("Serial number: \"%s%s%s\"\n", cs, eeprom->serial, cr);
cbus (0, e->c0_c1 & 0x0f);
cbus (1, e->c0_c1 >> 4);
cbus (2, e->c2_c3 & 0x0f);
cbus (3, e->c2_c3 >> 4);
cbus (4, e->c4 & 0x0f);
ck = 0xAAAA;
for (i=0; i<126; i+=2)
{
unsigned short v = eeprom->buf[i] + 256 * eeprom->buf[i+1];
ck ^= v;
ck = (ck << 1) | (ck >> 15);
}
if (e->checksum_low == (ck & 0xff)
&& e->checksum_high == (ck >> 8))
printf("Checksum: %sValid%s\n", cb, cr);
else
printf("Checksum: %sINVALID%s\n", cb, cr);
}
static void
dump_imagebuf (byte *buf)
{
int i, j;
for (i=0; i<MAX_EEPROM; i += 16)
{
printf("%02x : ", i);
for (j=i; j<i+16; j++)
{
if (j % 16 == 8)
putchar(' ');
color (buf[j] == 0xff ? 34 : buf[j] ? 32 : 0);
printf(" %02x", buf[j]);
}
color (0);
printf (" : ");
for (j=i; j<i+16; j++)
{
if (j % 16 == 8)
putchar(' ');
if (isgraph (buf[j]))
{
color (32);
putchar (buf[j]);
}
else
{
color (0);
putchar('.');
}
}
color (0);
printf(" : %02x\n", i);
}
}
/* ------------------------------------------------------------ */
static void
close_chip ()
{
ftdi_deinit (handle);
}
static void
choose_chip (int chosen)
{
int i;
int num_devices;
struct ftdi_device_list *device_info;
struct usb_device *u_chose = NULL;
Fail (ftdi_init (handle));
num_devices = Fail (ftdi_usb_find_all (handle, &device_info, 0x0403, 0x6001));
printf("%d device%s connected", num_devices, num_devices?"":"s");
if (num_devices > 1 && chosen >= 0)
printf(", you chose device %d", chosen);
printf("\n");
if (num_devices == 0)
{
printf("um, no FTDI devices present.\n");
exit(1);
}
for (i=0; i<num_devices; i++)
{
char manufacturer[200], description[200], serial[200];
struct usb_device *u = device_info->dev;
ftdi_usb_get_strings (handle, u, manufacturer, 200, description, 200,
serial, 200);
if (i == chosen)
u_chose = u;
printf("Dev %2d: ", i);
printf("Filename \"%s\", " , device_info->dev->filename);
printf("Manf \"%s\", ", manufacturer);
printf("Desc \"%s\", ", description);
printf("Ser# \"%s\"\n", serial);
device_info = device_info->next;
}
if (num_devices > 1 && !u_chose)
{
printf("Too many devices to choose from! Use \"-choose <n>\" to
choose\n");
exit (1);
}
if (u_chose)
Fail (ftdi_usb_open_dev (handle, u_chose));
else
Fail (ftdi_usb_open (handle, 0x0403, 0x6001));
atexit (close_chip);
}
static int
read_eeprom (byte *buf)
{
int s;
s = ftdi_read_eeprom_getsize (handle, buf, MAX_EEPROM);
return s;
}
static void
write_eeprom (byte *buf)
{
ftdi_write_eeprom (handle, buf);
}
static void
read_imagefile (char *filename, byte *buf)
{
FILE *f = fopen(filename, "rb");
if (!f)
{
perror (filename);
exit (1);
}
fread (buf, 1, MAX_EEPROM, f);
fclose (f);
}
static void
write_imagefile (char *filename, byte *buf)
{
FILE *f = fopen(filename, "wb");
if (!f)
{
perror (filename);
exit (1);
}
fwrite (buf, 1, MAX_EEPROM, f);
fclose (f);
}
static void
extract_string (EEPROM *e, int ofs, char *str)
{
int len;
ofs &= 0x7f;
len = e->buf[ofs] - 2;
if (len < 0)
{
*str = 0;
return;
}
ofs += 2;
while (len)
{
*str++ = e->buf[ofs];
ofs += 2;
len -= 2;
}
*str = 0;
}
static void
extract_strings (EEPROM *e)
{
extract_string (e, e->eeprom.manuf_str_ofs, e->manuf);
extract_string (e, e->eeprom.device_str_ofs, e->device);
extract_string (e, e->eeprom.serial_str_ofs, e->serial);
}
static int
store_string (EEPROM *e, int i, byte *ofs, byte *len, char *str)
{
int slen = strlen (str);
int save_ofs = i;
char *save_str = str;
*ofs = i | 0x80;
*len = slen * 2 + 2;
e->buf[i] = slen * 2 + 2;
e->buf[i+1] = 3;
i += 2;
while (slen)
{
e->buf[i] = *str++;
e->buf[i+1] = 0;
i += 2;
if (i > 125)
{
fprintf(stderr, "Error: string table overflow storing \"%s\" at %d\n",
save_str, save_ofs);
exit(1);
}
slen --;
}
return i;
}
static void
store_strings (EEPROM *e)
{
int i = min3 (e->eeprom.manuf_str_ofs,
e->eeprom.device_str_ofs,
e->eeprom.serial_str_ofs);
i &= 0x7f;
i = store_string (e, i, &e->eeprom.manuf_str_ofs, &e->eeprom.manuf_str_len,
e->manuf);
i = store_string (e, i, &e->eeprom.device_str_ofs, &e->eeprom.device_str_len,
e->device);
i = store_string (e, i, &e->eeprom.serial_str_ofs, &e->eeprom.serial_str_len,
e->serial);
}
static void
recompute_checksum (EEPROM *e)
{
int i;
unsigned short ck = 0xAAAA;
for (i=0; i<126; i+=2)
{
unsigned short v = e->buf[i] + 256 * e->buf[i+1];
ck ^= v;
ck = (ck << 1) | (ck >> 15);
}
e->eeprom.checksum_low = ck & 0xff;
e->eeprom.checksum_high = ck >> 8;
}
/* ------------------------------------------------------------ */
static void
usage (char *argv0)
{
int i;
fprintf (stderr, "Usage: EEProm [-choose <num>] [options] (short options in
parens)\n");
fprintf (stderr, "\nIf these are given, the device is updated:\n");
fprintf (stderr, " -cbus0 <purpose> choose cbus0 pin's purpose. Same for
1,2,3,4 (-0,-1,etc)\n");
fprintf(stderr, " ");
for (i=0; i<16; i++)
fprintf(stderr, " %s", cbus_purposes[i]);
fprintf(stderr, "\n");
fprintf (stderr, " -invert <pin,...> say which pins are inverted. Use
+pin or -pin to change\n");
fprintf (stderr, " just the listed, else all not listed
are cleared. (-i)\n");
fprintf(stderr, " ");
for (i=0; i<8; i++)
fprintf(stderr, " %s", inverts[i]);
fprintf(stderr, "\n");
fprintf (stderr, " -manuf[acturer] \"<str>\"\n");
fprintf (stderr, " -device \"<str>\"\n");
fprintf (stderr, " -serial \"<str>\" set the manufacturer, device, or
serial number strings\n");
fprintf (stderr, " -power <mAmps> set max power (-p)\n");
fprintf (stderr, " -self device is self-powered\n");
fprintf (stderr, " -bus device is bus-powered\n");
fprintf (stderr, "\nIf these are given, nothing is written back to the
device:\n");
fprintf (stderr, " -decode decode the eeprom rather than update
it\n");
fprintf (stderr, " -dump hex dump the eeprom rather than
update it\n");
fprintf (stderr, " -backup <file> back up the eeprom data to <file>\n");
fprintf (stderr, " -restore <file> restore the eeprom data from
<file>\n");
fprintf (stderr, "\nExamples:\n\n");
fprintf (stderr, " %s -choose 2 -invert +dtr,ri -device \"FT232R - R8C\" -2
clk48\n", argv0);
fprintf (stderr, " %s -decode\n", argv0);
fprintf (stderr, " %s -backup r8c-adapter.ee\n", argv0);
exit (1);
}
typedef enum {
OPT_MANUF,
OPT_DEVICE,
OPT_SERIAL,
OPT_SELF,
OPT_BUS,
OPT_BACKUP,
OPT_RESTORE,
OPT_DECODE,
OPT_DUMP
} OptionVals;
static struct option EEProm_options[] = {
{ "cbus0", 1, 0, '0' },
{ "cbus1", 1, 0, '1' },
{ "cbus2", 1, 0, '2' },
{ "cbus3", 1, 0, '3' },
{ "cbus4", 1, 0, '4' },
{ "invert", 1, 0, 'i' },
{ "manuf", 1, 0, OPT_MANUF },
{ "manufacturer", 1, 0, OPT_MANUF },
{ "device", 1, 0, OPT_DEVICE },
{ "serial", 1, 0, OPT_SERIAL },
{ "power", 1, 0, 'p' },
{ "self", 0, 0, OPT_SELF },
{ "bus", 0, 0, OPT_BUS },
{ "decode", 0, 0, OPT_DECODE },
{ "dump", 0, 0, OPT_DUMP },
{ "backup", 1, 0, OPT_BACKUP },
{ "restore", 1, 0, OPT_RESTORE },
{ NULL, 0, 0, 0 }
};
typedef enum {
INV_NONE, INV_ADD, INV_SUB
} Invert_Mode;
int
main(int argc, char **argv)
{
char optch;
EEProm *e;
int just_exit = 0;
int i, c;
int chosen = -1;
if (argc > 2
&& (strcmp (argv[1], "-choose") == 0
|| strcmp (argv[1], "--choose") == 0))
{
char *argv0 = argv[0];
chosen = atoi (argv[2]);
argv += 2;
argc -= 2;
argv[0] = argv0;
}
choose_chip (chosen);
rbuf.buf_size = read_eeprom (rbuf.buf);
extract_strings (&rbuf);
memcpy (&wbuf, &rbuf, sizeof(EEPROM));
e = & wbuf.eeprom;
while ((optch = getopt_long_only (argc, argv, "0:1:2:3:4:i:p:",
EEProm_options, NULL)) != -1)
{
switch (optch)
{
case OPT_DECODE:
store_strings (&wbuf);
recompute_checksum (&wbuf);
decode_imagebuf (&wbuf);
just_exit = 1;
break;
case OPT_DUMP:
store_strings (&wbuf);
recompute_checksum (&wbuf);
dump_imagebuf (wbuf.buf);
just_exit = 1;
break;
case OPT_BACKUP:
printf("backing up to %s\n", optarg);
write_imagefile (optarg, rbuf.buf);
exit (0);
case OPT_RESTORE:
printf("restoring from %s\n", optarg);
read_imagefile (optarg, wbuf.buf);
write_eeprom (wbuf.buf);
exit (0);
case '0':
case '1':
case '2':
case '3':
case '4':
c = optch - '0';
for (i=0; i<16; i++)
if (strcmp (optarg, cbus_purposes[i]) == 0)
break;
if (i == 16)
{
fprintf(stderr, "Error: \"%s\" is not a valid CBUS purpose:\n",
optarg);
for (i=0; i<16; i++)
fprintf(stderr, " %s", cbus_purposes[i]);
fprintf(stderr, "\n");
exit (1);
}
switch (c)
{
case 0:
e->c0_c1 = (e->c0_c1 & 0xf0) | i;
break;
case 1:
e->c0_c1 = (e->c0_c1 & 0x0f) | (i << 4);
break;
case 2:
e->c2_c3 = (e->c2_c3 & 0xf0) | i;
break;
case 3:
e->c2_c3 = (e->c2_c3 & 0x0f) | (i << 4);
break;
case 4:
e->c4 = i;
break;
}
break;
case 'i':
{
char *opt, save;
Invert_Mode imode;
imode = INV_NONE;
opt = optarg;
while (*opt)
{
if (*opt == '+')
{
imode = INV_ADD;
opt ++;
}
else if (*opt == '-')
{
imode = INV_SUB;
opt ++;
}
else
{
char *sp = opt;
while (*sp && *sp != ',')
sp++;
save = *sp;
*sp = 0;
for (i=0; i<8; i++)
if (strcmp (inverts[i], opt) == 0)
break;
if (i == 8)
{
fprintf(stderr, "Error: \"%s\" is not a valid
invertable pin:\n", opt);
for (i=0; i<8; i++)
fprintf(stderr, " %s", inverts[i]);
fprintf(stderr, "\n");
exit (1);
}
switch (imode)
{
case INV_NONE:
imode = INV_ADD;
e->invert = 0;
case INV_ADD:
e->invert |= 1 << i;
break;
case INV_SUB:
e->invert &= ~(1 << i);
break;
}
*sp = save;
opt = sp;
if (*opt == ',')
opt ++;
}
}
}
break;
case OPT_MANUF:
strcpy (wbuf.manuf, optarg);
break;
case OPT_DEVICE:
strcpy (wbuf.device, optarg);
break;
case OPT_SERIAL:
strcpy (wbuf.serial, optarg);
break;
case 'p':
e->max_power = atoi (optarg) / 2;
break;
case OPT_SELF:
e->config |= EE_CONFIG_SELFPOWER;
break;
case OPT_BUS:
e->config &= ~EE_CONFIG_SELFPOWER;
break;
default:
usage (argv[0]);
exit (0);
}
}
if (just_exit)
exit (0);
store_strings (&wbuf);
recompute_checksum (&wbuf);
if (memcmp (rbuf.buf, wbuf.buf, 127))
{
printf("writing out new EEProm...\n");
write_eeprom (wbuf.buf);
}
else
printf("no changes need to be written out\n");
return 0;
}
--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to libftdi+unsubscribe@xxxxxxxxxxxxxxxxxxxxxxx
|