diff --git a/tools/ftditools/Makefile b/tools/ftditools/Makefile new file mode 100644 index 000000000..d52b445e1 --- /dev/null +++ b/tools/ftditools/Makefile @@ -0,0 +1,10 @@ +LDFLAGS = -lftdi + +TARGETS = bbmc + +CFLAGS = -Wall -Wextra #-Werror + +all: $(TARGETS) + +clean: + rm $(TARGETS) diff --git a/tools/ftditools/bbmc.c b/tools/ftditools/bbmc.c new file mode 100644 index 000000000..4388b2a66 --- /dev/null +++ b/tools/ftditools/bbmc.c @@ -0,0 +1,462 @@ +/* control reset and VREF2 lines */ + +#include +#include +#include +#include + +#define DEBUG 0 + +#define low(x) (1 << x) +#define high(x) (1 << (x + 8)) + +#define REDBEE_ECONOTAG_RESET high(2) +#define REDBEE_ECONOTAG_VREF2L high(7) +#define REDBEE_ECONOTAG_VREF2H high(6) +#define REDBEE_ECONOTAG_INTERFACE INTERFACE_A + +#define REDBEE_USB_RESET high(2) +#define REDBEE_USB_VREF2L low(5) +#define REDBEE_USB_VREF2H low(6) +#define REDBEE_USB_INTERFACE INTERFACE_B + +#define BOARD REDBEE_USB + +#define STR(x) #x +#define STR2(x) STR(x) +#define CAT(x,y) x##y +#define CAT2(x, y, z) x##y##z + +#define dir(x) ( CAT(x,_RESET) | CAT(x,_VREF2L) | CAT(x,_VREF2H)) +#define interface(x) ( CAT(x,_INTERFACE) ) +#define reset_release(x) ( CAT(x,_RESET) ) +#define reset_set(x) ( 0 ) +#define vref2_normal(x) ( CAT(x,_VREF2H) ) +#define vref2_erase(x) ( CAT(x,_VREF2L) ) + +/* fgets input buffer length: for prompts and such */ +#define BUF_LEN 32 + +struct layout { + char *name; + char *desc; + enum ftdi_interface interface; + uint16_t dir; + uint16_t reset_release; + uint16_t reset_set; + uint16_t vref2_normal; + uint16_t vref2_erase; +}; + +int print_and_prompt( struct ftdi_device_list *devlist ); +int bb_mpsee(struct ftdi_context *ftdic, uint16_t dir, uint16_t val); +void reset(struct ftdi_context *ftdic, const struct layout * l); +void erase(struct ftdi_context *ftdic, const struct layout * l); +void usage(void); + +#define std_layout(x) \ + .interface = interface(x), \ + .dir = dir(x), \ + .reset_release = reset_release(x), \ + .reset_set = reset_set(x), \ + .vref2_normal = vref2_normal(x), \ + .vref2_erase = vref2_erase(x), + +static struct layout layouts[] = +{ + { .name = "redbee_econotag", + .desc = "Redbee Econotag", + std_layout(REDBEE_ECONOTAG) + }, + { .name = "redbee_usb", + .desc = "Redbee USB stick", + std_layout(REDBEE_USB) + }, + { .name = NULL, /* end of table */ }, +}; + +struct command { + char *name; + char *desc; + void (*cmd)(struct ftdi_context *ftdic, const struct layout * l); +}; + +static const struct command commands[] = +{ + { + .name = "reset", + .desc = "Toggles reset pin", + .cmd = reset, + }, + { + .name = "erase", + .desc = "Sets VREF2 erase mode; toggles reset; waits 2 sec.; sets normal; toggles reset again", + .cmd = erase, + }, + { .name = NULL, /* end of table */ }, +}; + +struct layout * find_layout(char * str) +{ + uint32_t i = 0; + + while(layouts[i].name != NULL) { + if(strcmp(layouts[i].name, str) == 0) { return &layouts[i]; } + i++; + } + + return NULL; +} + +static uint32_t vendid = 0x0403; uint32_t prodid = 0x6010; + +int main(int argc, char **argv) +{ + struct ftdi_context ftdic; + struct ftdi_device_list *devlist; + int dev_index = -1; int num_devs; + char layout_str[BUF_LEN]; + struct layout layout; + struct layout *l = NULL; + int i, ret; + + /* overrides for layout parameters */ + int interface = -1; + int dir = -1; + int reset_release = -1; + int reset_set = -1; + int vref2_normal = -1; + int vref2_erase = -1; + + layout.name = NULL; + + while (1) { + int c; + int option_index = 0; + static struct option long_options[] = { + {"layout", required_argument, 0, 'l'}, + {"index", required_argument, 0, 'i'}, + {"vendor", required_argument, 0, 'v'}, + {"product", required_argument, 0, 'p'}, + {"dir", required_argument, 0, 0 }, + {"reset_release", required_argument, 0, 0 }, + {"reset_set", required_argument, 0, 0 }, + {"vref2_normal", required_argument, 0, 0 }, + {"vref2_erase", required_argument, 0, 0 }, + {"interface", required_argument, 0, 0 }, + {"help", no_argument, 0, '?'}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "i:l:v:p:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + /* process long opts */ + case 0: + if(strcmp(long_options[option_index].name, "interface") == 0) { + sscanf(optarg, "%i", &interface); + } + if(strcmp(long_options[option_index].name, "dir") == 0) { + sscanf(optarg, "%i", &dir); + } + if (strcmp(long_options[option_index].name, "reset_release") == 0) { + sscanf(optarg, "%i", &reset_release); + } + if (strcmp(long_options[option_index].name, "reset_set") == 0) { + sscanf(optarg, "%i", &reset_set); + } + if (strcmp(long_options[option_index].name, "vref2_normal") == 0) { + sscanf(optarg, "%i", &vref2_normal); + } + if (strcmp(long_options[option_index].name, "vref2_erase") == 0) { + sscanf(optarg, "%i", &vref2_erase); + } + break; + + case 'l': + strncpy(layout_str, optarg, BUF_LEN); + break; + case 'i': + dev_index = atoi(optarg); + break; + case 'v': + sscanf(optarg, "%i", &vendid); + break; + case 'p': + sscanf(optarg, "%i", &prodid); + break; + default: + usage(); + break; + } + } + + if( !(l = find_layout(layout_str)) && + !((interface >= 0) && + (dir >= 0) && + (reset_release >= 0) && + (reset_set >= 0) && + (vref2_normal >= 0) && + (vref2_erase >= 0)) + ) { + + printf("*** You must specify a layout or a complete set of overrides\n"); + return EXIT_FAILURE; + } + + if(l) { + memcpy(&layout, l, sizeof(struct layout)); + } + +#define override(x) if(x > 0) { layout.x = x; } + override(interface); + override(dir); + override(reset_release); override(reset_set); + override(vref2_normal); override(vref2_erase); + + if ((num_devs = ftdi_usb_find_all(&ftdic, &devlist, vendid, prodid)) < 0) + { + fprintf(stderr, "ftdi_usb_find_all failed: %d (%s)\n", + num_devs, + ftdi_get_error_string(&ftdic)); + return EXIT_FAILURE; + } + + if (ftdi_init(&ftdic) < 0) + { + fprintf(stderr, "ftdi_init failed\n"); + return EXIT_FAILURE; + } + + if (ftdi_set_interface(&ftdic, layout.interface) < 0) { + fprintf(stderr, "couldn't set interface %d\n", layout.interface); + return EXIT_FAILURE; + } + + printf("Found %d devices with vendor id 0x%04x product id 0x%04x\n", + num_devs, vendid, prodid); + + if(num_devs == 0) { return EXIT_SUCCESS; } + + if(num_devs == 1) { dev_index = 0; } + while( (dev_index < 0) || (dev_index >= num_devs)){ + dev_index = print_and_prompt(devlist); + } + + if(layout.name != NULL) { + printf("Opening device %d interface %d using layout %s\n", + dev_index, layout.interface, layout.name); + } else { + printf("Opening device %d interface %d without a layout.\n", + dev_index, layout.interface); + } + + if( (ret = ftdi_usb_open_desc_index( + &ftdic, + vendid, + prodid, + NULL, + NULL, + dev_index)) < 0) { + fprintf(stderr, "couldn't open dev_index %d\n", dev_index); + return EXIT_FAILURE; + } + + + for(i = 0; commands[i].name != NULL; i++) { + if( (argv[optind] != NULL) && + (strcmp(commands[i].name, argv[optind]) == 0)) { break; } + } + if(commands[i].name != NULL) { + commands[i].cmd(&ftdic, &layout); + } else { + printf("invalid command\n"); + + ftdi_list_free(&devlist); + ftdi_deinit(&ftdic); + + return EXIT_FAILURE; + } + + printf("done.\n"); + + ftdi_list_free(&devlist); + ftdi_deinit(&ftdic); + + return EXIT_SUCCESS; +} + +void usage(void) +{ + int i; + printf( "Usage: bbmc [options|overrides] -l|--layout layout command \n"); + printf( "Commands:\n"); + for(i = 0; commands[i].name != NULL; i++) { + printf( " %s: %s\n", commands[i].name, commands[i].desc); + } + printf("\n"); + printf( "Required options:\n"); + printf( " -l|--layout\t specifiy which board layout to use\n"); + printf( " \t layout is not necessary with a full\n"); + printf( " \t set of overrides\n"); + printf( "\nLayout overrides:\n"); + printf( " --interface\t\t FTDI interface to use\n"); + printf( " --dir\t\t direction (1 is output)\n"); + printf( " --reset_release\t reset release command\n"); + printf( " --reset_set\t\t reset set command\n"); + printf( " --vref2_normal\t vref2 normal\n"); + printf( " --vref2_erase\t vref2 erase\n"); + printf("\n"); + printf( "Layouts:\n"); + for(i = 0; layouts[i].name != NULL; i++) { + printf( "\t%s: %s\n", layouts[i].name, layouts[i].desc); + printf("\n"); + printf( "\t\tinterface: \t0x%04x\n", layouts[i].interface); + printf( "\t\tdir: \t\t0x%04x\n", layouts[i].dir); + printf( "\t\treset release: \t0x%04x\n", layouts[i].reset_release); + printf( "\t\treset hold: \t0x%04x\n", layouts[i].reset_set); + printf( "\t\tvref2 normal: \t0x%04x\n", layouts[i].vref2_normal); + printf( "\t\tvref2 erase: \t0x%04x\n", layouts[i].vref2_erase); + printf("\n"); + } + printf("\n"); + printf( "Options:\n"); + printf( " -i|--index specifiy which device to use (default 0)\n"); + printf( " -v|--vendor set vendor id (default 0x0403)\n"); + printf( " -p|--prodcut set vendor id (default 0x6010)\n"); +} + +int print_and_prompt( struct ftdi_device_list *devlist ) +{ + int i, ret; + struct ftdi_context ftdic; + struct ftdi_device_list *curdev; + char manufacturer[128], description[128], serial[128]; + char input[BUF_LEN]; char *s; + int sel = -1; + + printf("\n"); + + i = 0; + for (curdev = devlist; curdev != NULL; i++) + { + printf(" [%d] ", i); + if (0 > (ret = ftdi_usb_get_strings(&ftdic, + curdev->dev, + manufacturer, 128, + description, 128, + serial, 128))) + { + fprintf(stderr, "ftdi_usb_get_strings failed: %d (%s)\n", + ret, ftdi_get_error_string(&ftdic)); + return EXIT_FAILURE; + } + printf("Manufacturer: %s, Description: %s, Serial %s\n", + manufacturer, description, serial); + curdev = curdev->next; + } + + printf("\nUse which device? "); + + s = fgets(input, BUF_LEN, stdin); + if (s != NULL) { + size_t last = strlen (input) - 1; + if (input[last] == '\n') input[last] = '\0'; + } + + sscanf(s, "%i",&sel); + + return sel; +} + +void reset(struct ftdi_context *ftdic, const struct layout * l) +{ + + /* using MPSSE since it give access to high GPIO*/ + /* set as inputs for now */ + ftdi_set_bitmode(ftdic, 0 , BITMODE_MPSSE); + + printf("toggle reset\n"); + + bb_mpsee(ftdic, l->dir, (l->reset_release | l->vref2_normal)); + bb_mpsee(ftdic, l->dir, (l->reset_set | l->vref2_normal)); + bb_mpsee(ftdic, l->dir, (l->reset_release | l->vref2_normal)); + + return; + +} + + +void erase(struct ftdi_context *ftdic, const struct layout * l) +{ + printf("setting VREF2 erase\n"); + + /* using MPSSE since it give access to high GPIO*/ + /* set as inputs for now */ + ftdi_set_bitmode(ftdic, 0 , BITMODE_MPSSE); + + bb_mpsee(ftdic, l->dir, (l->reset_release | l->vref2_normal)); + bb_mpsee(ftdic, l->dir, (l->reset_release | l->vref2_erase)); + + printf("toggle reset\n"); + + bb_mpsee(ftdic, l->dir, (l->reset_set | l->vref2_erase)); + bb_mpsee(ftdic, l->dir, (l->reset_release | l->vref2_erase)); + + printf("waiting for erase\n"); + + sleep(1); + + printf("setting VREF2 normal\n"); + + bb_mpsee(ftdic, l->dir, (l->reset_release | l->vref2_normal)); + + reset(ftdic, l); + + return; + +} + + +int bb_mpsee(struct ftdi_context *ftdic, uint16_t dir, uint16_t val) +{ + uint8_t buf[3]; + int ret; + + /* command "set data bits low byte" */ + buf[0] = 0x80; + buf[1] = (val & 0xff); + buf[2] = dir & 0xff; +#if DEBUG + fprintf(stderr,"write %x %x %x\n",buf[0],buf[1],buf[2]); +#endif + + if ((ret = (ftdi_write_data(ftdic, buf, 3))) < 0) + { + perror("ft2232_write error"); + fprintf(stderr, "ft2232_write command %x\n", buf[0]); + return EXIT_FAILURE; + } + + + /* command "set data bits high byte" */ + buf[0] = 0x82; + buf[1] = (val >> 8); + buf[2] = dir >> 8; +#if DEBUG + fprintf(stderr,"write %x %x %x\n",buf[0],buf[1],buf[2]); +#endif + + if ((ret = (ftdi_write_data(ftdic, buf, 3))) < 0) + { + perror("ft2232_write error"); + fprintf(stderr, "ft2232_write command %x\n", buf[0]); + return EXIT_FAILURE; + } + + return 0; + +}