/* * avrdude - A Downloader/Uploader for AVR device programmers * * avrdude support for The Bus Pirate - universal serial interface * * Copyright (C) 2009 Michal Ludvig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * BusPirate AVR Chip * --------- -------- * GND <-> GND * +5V <-> Vcc * CS <-> RESET * MOSI <-> MOSI * MISO <-> MISO * SCL/CLK <-> SCK * * Tested with BusPirate PTH, firmware version 2.1 programming ATmega328P */ /* $Id$ */ #include "ac_cfg.h" #include #include #include #include #if defined(WIN32NATIVE) # include /* for alloca() */ #endif #include "avrdude.h" #include "avr.h" #include "pgm.h" #include "serial.h" /* ====== Private data structure ====== */ /* CS and AUX pin bitmasks in * 0100wxyz - Configure peripherals command */ #define BP_RESET_CS 0x01 #define BP_RESET_AUX 0x02 #define BP_RESET_AUX2 0x04 #define BP_FLAG_IN_BINMODE (1<<0) #define BP_FLAG_XPARM_FORCE_ASCII (1<<1) #define BP_FLAG_XPARM_RESET (1<<2) #define BP_FLAG_XPARM_SPIFREQ (1<<3) struct pdata { char hw_version[10]; int fw_version; /* = 100*fw_major + fw_minor */ int binmode_version; int bin_spi_version; int current_peripherals_config; int spifreq; /* 0..7 - see buspirate manual for what freq each value means */ int reset; /* See BP_RESET_* above */ }; #define PDATA(pgm) ((struct pdata *)(pgm->cookie)) /* Binary mode is available from firmware v2.7 on */ #define FW_BINMODE_VER 207 /* ====== Feature checks ====== */ static inline int buspirate_has_aux2(struct programmer_t *pgm) { return ((PDATA(pgm)->fw_version >= 300) && strcmp(PDATA(pgm)->hw_version, "v1a") == 0); } static inline int buspirate_uses_ascii(struct programmer_t *pgm) { return (pgm->flag & BP_FLAG_XPARM_FORCE_ASCII) || (PDATA(pgm)->fw_version < FW_BINMODE_VER); } /* ====== Serial talker functions - binmode ====== */ static void dump_mem(char *buf, size_t len) { size_t i; for (i = 0; i 1) { fprintf(stderr, "%s: buspirate_send_bin():\n", progname); dump_mem(data, len); } rc = serial_send(&pgm->fd, (unsigned char *)data, len); return rc; } static int buspirate_recv_bin(struct programmer_t *pgm, char *buf, size_t len) { int rc; rc = serial_recv(&pgm->fd, (unsigned char *)buf, len); if (rc < 0) return EOF; if (verbose > 1) { fprintf(stderr, "%s: buspirate_recv_bin():\n", progname); dump_mem(buf, len); } return len; } static int buspirate_expect_bin(struct programmer_t *pgm, char *send_data, size_t send_len, char *expect_data, size_t expect_len) { char *recv_buf = alloca(expect_len); if (!pgm->flag & BP_FLAG_IN_BINMODE) { fprintf(stderr, "BusPirate: Internal error: buspirate_send_bin() called from ascii mode"); exit(1); } buspirate_send_bin(pgm, send_data, send_len); buspirate_recv_bin(pgm, recv_buf, expect_len); if (memcmp(expect_data, recv_buf, expect_len) != 0) return 0; return 1; } static int buspirate_expect_bin_byte(struct programmer_t *pgm, char send_byte, char expect_byte) { return buspirate_expect_bin(pgm, &send_byte, 1, &expect_byte, 1); } /* ====== Serial talker functions - ascii mode ====== */ static int buspirate_getc(struct programmer_t *pgm) { int rc; unsigned char ch = 0; if (pgm->flag & BP_FLAG_IN_BINMODE) { fprintf(stderr, "BusPirate: Internal error: buspirate_getc() called from binmode"); exit(1); } rc = serial_recv(&pgm->fd, &ch, 1); if (rc < 0) return EOF; return ch; } static char *buspirate_readline_noexit(struct programmer_t *pgm, char *buf, size_t len) { char *buf_p; long orig_serial_recv_timeout = serial_recv_timeout; /* Static local buffer - this may come handy at times */ static char buf_local[100]; if (buf == NULL) { buf = buf_local; len = sizeof(buf_local); } buf_p = buf; memset(buf, 0, len); while (buf_p < (buf + len - 1)) { /* keep the very last byte == 0 */ *buf_p = buspirate_getc(pgm); if (*buf_p == '\r') continue; if (*buf_p == '\n') break; if (*buf_p == EOF) { *buf_p = '\0'; break; } buf_p++; serial_recv_timeout = 100; } serial_recv_timeout = orig_serial_recv_timeout; if (verbose) fprintf(stderr, "%s: buspirate_readline(): %s%s", progname, buf, buf[strlen(buf) - 1] == '\n' ? "" : "\n"); if (! buf[0]) return NULL; return buf; } static char *buspirate_readline(struct programmer_t *pgm, char *buf, size_t len) { char *ret; ret = buspirate_readline_noexit(pgm, buf, len); if (! ret) { fprintf(stderr, "%s: buspirate_readline(): programmer is not responding\n", progname); exit(1); } return ret; } static int buspirate_send(struct programmer_t *pgm, char *str) { int rc; if (verbose) fprintf(stderr, "%s: buspirate_send(): %s", progname, str); if (pgm->flag & BP_FLAG_IN_BINMODE) { fprintf(stderr, "BusPirate: Internal error: buspirate_send() called from binmode"); exit(1); } rc = serial_send(&pgm->fd, (unsigned char *)str, strlen(str)); if (rc) return rc; while (strcmp(buspirate_readline(pgm, NULL, 0), str) != 0) /* keep reading until we get what we sent there */ ; /* by now we should be in sync */ return 0; } static int buspirate_is_prompt(char *str) { /* Prompt ends with '>' or '> ' * all other input probably ends with '\n' */ return (str[strlen(str) - 1] == '>' || str[strlen(str) - 2] == '>'); } static int buspirate_expect(struct programmer_t *pgm, char *send, char *expect, int wait_for_prompt) { int got_it = 0; size_t expect_len = strlen(expect); char *rcvd; buspirate_send(pgm, send); while (1) { rcvd = buspirate_readline(pgm, NULL, 0); if (strncmp(rcvd, expect, expect_len) == 0) got_it = 1; if (buspirate_is_prompt(rcvd)) break; } return got_it; } /* ====== Do-nothing functions ====== */ static void buspirate_dummy_6(struct programmer_t *pgm, const char *p) { } /* ====== Config / parameters handling functions ====== */ static int buspirate_parseextparms(struct programmer_t *pgm, LISTID extparms) { LNODEID ln; const char *extended_param; char reset[10]; char *preset = reset; /* for strtok() */ int spifreq; for (ln = lfirst(extparms); ln; ln = lnext(ln)) { extended_param = ldata(ln); if (strcmp(extended_param, "ascii") == 0) { pgm->flag |= BP_FLAG_XPARM_FORCE_ASCII; continue; } if (sscanf(extended_param, "spifreq=%d", &spifreq) == 1) { if (spifreq & (~0x07)) { fprintf(stderr, "BusPirate: spifreq must be between 0 and 7.\n"); fprintf(stderr, "BusPirate: see BusPirate manual for details.\n"); return -1; } PDATA(pgm)->spifreq = spifreq; pgm->flag |= BP_FLAG_XPARM_SPIFREQ; continue; } if (sscanf(extended_param, "reset=%s", reset) == 1) { char *resetpin; while ((resetpin = strtok(preset, ","))) { preset = NULL; /* for subsequent strtok() calls */ if (strcasecmp(resetpin, "cs") == 0) PDATA(pgm)->reset |= BP_RESET_CS; else if (strcasecmp(resetpin, "aux") == 0 || strcasecmp(reset, "aux1") == 0) PDATA(pgm)->reset |= BP_RESET_AUX; else if (strcasecmp(resetpin, "aux2") == 0) PDATA(pgm)->reset |= BP_RESET_AUX2; else { fprintf(stderr, "BusPirate: reset must be either CS or AUX.\n"); return -1; } } pgm->flag |= BP_FLAG_XPARM_RESET; continue; } } return 0; } static int buspirate_verifyconfig(struct programmer_t *pgm) { /* Default reset pin is CS */ if (PDATA(pgm)->reset == 0x00) PDATA(pgm)->reset |= BP_RESET_CS; /* reset=AUX2 is only available on HW=v1a and FW>=3.0 */ if ((PDATA(pgm)->reset & BP_RESET_AUX2) && !buspirate_has_aux2(pgm)) { fprintf(stderr, "BusPirate: Pin AUX2 is only available in binary mode\n"); fprintf(stderr, "BusPirate: with hardware==v1a && firmware>=3.0\n"); fprintf(stderr, "BusPirate: Your hardware==%s and firmware==%d.%d\n", PDATA(pgm)->hw_version, PDATA(pgm)->fw_version/100, PDATA(pgm)->fw_version%100); return -1; } if ((PDATA(pgm)->reset != BP_RESET_CS) && buspirate_uses_ascii(pgm)) { fprintf(stderr, "BusPirate: RESET pin other than CS is not supported in ASCII mode\n"); return -1; } if ((pgm->flag & BP_FLAG_XPARM_SPIFREQ) && buspirate_uses_ascii(pgm)) { fprintf(stderr, "BusPirate: SPI speed selection is not supported in ASCII mode\n"); return -1; } return 0; } /* ====== Programmer methods ======= */ static int buspirate_open(struct programmer_t *pgm, char * port) { /* BusPirate runs at 115200 by default */ if(pgm->baudrate == 0) pgm->baudrate = 115200; strcpy(pgm->port, port); if (serial_open(port, pgm->baudrate, &pgm->fd)==-1) { return -1; } /* drain any extraneous input */ serial_drain(&pgm->fd, 0); return 0; } static void buspirate_close(struct programmer_t *pgm) { serial_close(&pgm->fd); pgm->fd.ifd = -1; } static void buspirate_reset_from_binmode(struct programmer_t *pgm) { char buf[10]; buf[0] = 0x00; /* BinMode: revert to HiZ */ buspirate_send_bin(pgm, buf, 1); buf[0] = 0x0F; /* BinMode: reset */ buspirate_send_bin(pgm, buf, 1); /* read back all output */ memset(buf, '\0', sizeof(buf)); for (;;) { int rc; rc = buspirate_recv_bin(pgm, buf, sizeof(buf) - 1); if (buspirate_is_prompt(buf)) { pgm->flag &= ~BP_FLAG_IN_BINMODE; break; } if (rc == EOF) break; memset(buf, '\0', sizeof(buf)); } if (pgm->flag & BP_FLAG_IN_BINMODE) { fprintf(stderr, "BusPirate reset failed. You may need to powercycle it.\n"); exit(1); } if (verbose) fprintf(stderr, "BusPirate is back in the text mode\n"); } static int buspirate_start_spi_mode_bin(struct programmer_t *pgm) { char buf[20] = { '\0' }; /* == Switch to binmode - send 20x '\0' == */ buspirate_send_bin(pgm, buf, sizeof(buf)); /* Expecting 'BBIOx' reply */ memset(buf, 0, sizeof(buf)); buspirate_recv_bin(pgm, buf, 5); if (sscanf(buf, "BBIO%d", &PDATA(pgm)->binmode_version) != 1) { fprintf(stderr, "Binary mode not confirmed: '%s'\n", buf); buspirate_reset_from_binmode(pgm); return -1; } if (verbose) fprintf(stderr, "BusPirate binmode version: %d\n", PDATA(pgm)->binmode_version); pgm->flag |= BP_FLAG_IN_BINMODE; /* == Enter SPI mode == */ buf[0] = 0x01; /* Enter raw SPI mode */ buspirate_send_bin(pgm, buf, 1); memset(buf, 0, sizeof(buf)); buspirate_recv_bin(pgm, buf, 4); if (sscanf(buf, "SPI%d", &PDATA(pgm)->bin_spi_version) != 1) { fprintf(stderr, "SPI mode not confirmed: '%s'\n", buf); buspirate_reset_from_binmode(pgm); return -1; } if (verbose) fprintf(stderr, "BusPirate SPI version: %d\n", PDATA(pgm)->bin_spi_version); /* 0b0100wxyz - Configure peripherals w=power, x=pull-ups/aux2, y=AUX, z=CS * we want power (0x48) and all reset pins high. */ PDATA(pgm)->current_peripherals_config = 0x48; PDATA(pgm)->current_peripherals_config |= BP_RESET_CS; PDATA(pgm)->current_peripherals_config |= BP_RESET_AUX; if (buspirate_has_aux2(pgm)) PDATA(pgm)->current_peripherals_config |= BP_RESET_AUX2; buspirate_expect_bin_byte(pgm, PDATA(pgm)->current_peripherals_config, 0x01); usleep(50000); // sleep for 50ms after power up /* 01100xxx - SPI speed * xxx = 000=30kHz, 001=125kHz, 010=250kHz, 011=1MHz, * 100=2MHz, 101=2.6MHz, 110=4MHz, 111=8MHz * use 30kHz = 0x60 */ buspirate_expect_bin_byte(pgm, 0x60 | PDATA(pgm)->spifreq, 0x01); /* 1000wxyz - SPI config, w=HiZ(0)/3.3v(1), x=CLK idle, y=CLK edge, z=SMP sample * we want: 3.3V(1), idle low(0), data change on trailing edge (1), * sample in the middle of the pulse (0) * => 0b10001010 = 0x8a */ buspirate_expect_bin_byte(pgm, 0x8A, 0x01); return 0; } static int buspirate_start_spi_mode_ascii(struct programmer_t *pgm) { int spi_cmd = -1; int cmd; char *rcvd, mode[11], buf[5]; buspirate_send(pgm, "M\n"); while(1) { rcvd = buspirate_readline(pgm, NULL, 0); if (spi_cmd == -1 && sscanf(rcvd, "%d. %10s", &cmd, mode)) { if (strcmp(mode, "SPI") == 0) spi_cmd = cmd; } if (buspirate_is_prompt(rcvd)) break; } if (spi_cmd == -1) { fprintf(stderr, "%s: SPI mode number not found. Does your BusPirate support SPI?\n", progname); fprintf(stderr, "%s: Try powercycling your BusPirate and try again.\n", progname); return -1; } snprintf(buf, sizeof(buf), "%d\n", spi_cmd); buspirate_send(pgm, buf); buf[0] = '\0'; while (1) { rcvd = buspirate_readline(pgm, NULL, 0); if (strstr(rcvd, "Normal (H=3.3V, L=GND)")) { /* BP firmware 2.1 defaults to Open-drain output. * That doesn't work on my board, even with pull-up * resistors. Select 3.3V output mode instead. */ sscanf(rcvd, " %d.", &cmd); snprintf(buf, sizeof(buf), "%d\n", cmd); } if (buspirate_is_prompt(rcvd)) { if (strncmp(rcvd, "SPI>", 4) == 0) { fprintf(stderr, "BusPirate is now configured for SPI\n"); break; } /* Not yet 'SPI>' prompt */ if (buf[0]) { buspirate_send(pgm, buf); buf[0] = '\0'; } else buspirate_send(pgm, "\n"); } } return 0; } static void buspirate_enable(struct programmer_t *pgm) { unsigned char *reset_str = "#\n"; unsigned char *accept_str = "y\n"; char *rcvd; int fw_v1 = 0, fw_v2 = 0; int rc, print_banner = 0; fprintf(stderr, "Detecting BusPirate...\n"); /* Call buspirate_send_bin() instead of buspirate_send() * because we don't know if BP is in text or bin mode */ rc = buspirate_send_bin(pgm, reset_str, strlen(reset_str)); if (rc) { fprintf(stderr, "BusPirate is not responding. Serial port error: %d\n", rc); exit(1); } while(1) { rcvd = buspirate_readline_noexit(pgm, NULL, 0); if (! rcvd) { fprintf(stderr, "BusPirate is not responding. Attempting reset.\n"); buspirate_reset_from_binmode(pgm); /* re-run buspirate_enable() */ buspirate_enable(pgm); return; } if (strncmp(rcvd, "Are you sure?", 13) == 0) { buspirate_send_bin(pgm, accept_str, strlen(accept_str)); } if (strncmp(rcvd, "RESET", 5) == 0) { print_banner = 1; continue; } if (buspirate_is_prompt(rcvd)) { puts("**"); break; } sscanf(rcvd, "Bus Pirate %9s", PDATA(pgm)->hw_version); sscanf(rcvd, "Firmware v%d.%d", &fw_v1, &fw_v2); if (print_banner) fprintf(stderr, "** %s", rcvd); } PDATA(pgm)->fw_version = 100 * fw_v1 + fw_v2; if (PDATA(pgm)->hw_version[0] == 0 || PDATA(pgm)->fw_version == 0) { fprintf(stderr, "BusPirate not detected. Aborting.\n"); exit(1); } if (buspirate_verifyconfig(pgm) < 0) exit(1); if (PDATA(pgm)->fw_version >= FW_BINMODE_VER && !(pgm->flag & BP_FLAG_XPARM_FORCE_ASCII)) { fprintf(stderr, "BusPirate: using BINARY mode\n"); if (buspirate_start_spi_mode_bin(pgm) < 0) fprintf(stderr, "%s: Failed to start binary SPI mode\n", progname); } if (!pgm->flag & BP_FLAG_IN_BINMODE) { fprintf(stderr, "BusPirate: using ASCII mode\n"); if (buspirate_start_spi_mode_ascii(pgm) < 0) { fprintf(stderr, "%s: Failed to start ascii SPI mode\n", progname); exit(1); } } } static void buspirate_disable(struct programmer_t *pgm) { if (pgm->flag & BP_FLAG_IN_BINMODE) { serial_recv_timeout = 100; buspirate_reset_from_binmode(pgm); } else buspirate_expect(pgm, "#\n", "RESET", 1); } static int buspirate_initialize(struct programmer_t *pgm, AVRPART * p) { pgm->powerup(pgm); return pgm->program_enable(pgm, p); } static void buspirate_powerup(struct programmer_t *pgm) { if (pgm->flag & BP_FLAG_IN_BINMODE) { /* Powerup in BinMode is handled in SPI init */ return; } else if (buspirate_expect(pgm, "W\n", "POWER SUPPLIES ON", 1)) return; fprintf(stderr, "%s: warning: did not get a response to PowerUp command.\n", progname); fprintf(stderr, "%s: warning: Trying to continue anyway...\n", progname); } static void buspirate_powerdown(struct programmer_t *pgm) { if (pgm->flag & BP_FLAG_IN_BINMODE) { /* 0b0100wxyz - Configure peripherals w=power, x=pull-ups, y=AUX, z=CS * we want everything off -- 0b01000000 = 0x40 */ if (buspirate_expect_bin_byte(pgm, 0x40, 0x01)) return; } else if (buspirate_expect(pgm, "w\n", "POWER SUPPLIES OFF", 1)) return; fprintf(stderr, "%s: warning: did not get a response to PowerDown command.\n", progname); } static int buspirate_cmd_bin(struct programmer_t *pgm, unsigned char cmd[4], unsigned char res[4]) { /* 0001xxxx - Bulk SPI transfer, send/read 1-16 bytes (0=1byte!) * we are sending 4 bytes -> 0x13 */ if (!buspirate_expect_bin_byte(pgm, 0x13, 0x01)) return -1; buspirate_send_bin(pgm, (char *)cmd, 4); buspirate_recv_bin(pgm, (char *)res, 4); return 0; } static int buspirate_cmd_ascii(struct programmer_t *pgm, unsigned char cmd[4], unsigned char res[4]) { char buf[25]; char *rcvd; int spi_write, spi_read, i = 0; snprintf(buf, sizeof(buf), "0x%02x 0x%02x 0x%02x 0x%02x\n", cmd[0], cmd[1], cmd[2], cmd[3]); buspirate_send(pgm, buf); while (i < 4) { rcvd = buspirate_readline(pgm, NULL, 0); /* WRITE: 0xAC READ: 0x04 */ if (sscanf(rcvd, "WRITE: 0x%x READ: 0x%x", &spi_write, &spi_read) == 2) { res[i++] = spi_read; } if (buspirate_is_prompt(rcvd)) break; } if (i != 4) { fprintf(stderr, "%s: error: SPI has not read 4 bytes back\n", progname); return -1; } /* wait for prompt */ while (buspirate_getc(pgm) != '>') /* do nothing */; return 0; } static int buspirate_cmd(struct programmer_t *pgm, unsigned char cmd[4], unsigned char res[4]) { if (pgm->flag & BP_FLAG_IN_BINMODE) return buspirate_cmd_bin(pgm, cmd, res); else return buspirate_cmd_ascii(pgm, cmd, res); } static int buspirate_program_enable(struct programmer_t *pgm, AVRPART * p) { unsigned char cmd[4]; unsigned char res[4]; if (pgm->flag & BP_FLAG_IN_BINMODE) { /* Clear configured reset pin(s): CS and/or AUX and/or AUX2 */ PDATA(pgm)->current_peripherals_config &= ~PDATA(pgm)->reset; buspirate_expect_bin_byte(pgm, PDATA(pgm)->current_peripherals_config, 0x01); } else buspirate_expect(pgm, "{\n", "CS ENABLED", 1); if (p->op[AVR_OP_PGM_ENABLE] == NULL) { fprintf(stderr, "program enable instruction not defined for part \"%s\"\n", p->desc); return -1; } memset(cmd, 0, sizeof(cmd)); avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd); pgm->cmd(pgm, cmd, res); if (res[2] != cmd[1]) return -2; return 0; } static int buspirate_chip_erase(struct programmer_t *pgm, AVRPART * p) { unsigned char cmd[4]; unsigned char res[4]; if (p->op[AVR_OP_CHIP_ERASE] == NULL) { fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n", p->desc); return -1; } pgm->pgm_led(pgm, ON); memset(cmd, 0, sizeof(cmd)); avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd); pgm->cmd(pgm, cmd, res); usleep(p->chip_erase_delay); pgm->initialize(pgm, p); pgm->pgm_led(pgm, OFF); return 0; } void buspirate_initpgm(struct programmer_t *pgm) { strcpy(pgm->type, "BusPirate"); pgm->display = buspirate_dummy_6; /* BusPirate itself related methods */ pgm->open = buspirate_open; pgm->close = buspirate_close; pgm->enable = buspirate_enable; pgm->disable = buspirate_disable; pgm->initialize = buspirate_initialize; /* Chip related methods */ pgm->powerup = buspirate_powerup; pgm->powerdown = buspirate_powerdown; pgm->program_enable = buspirate_program_enable; pgm->chip_erase = buspirate_chip_erase; pgm->cmd = buspirate_cmd; pgm->read_byte = avr_read_byte_default; pgm->write_byte = avr_write_byte_default; /* Support functions */ pgm->parseextparams = buspirate_parseextparms; /* Allocate private data */ if ((pgm->cookie = calloc(1, sizeof(struct pdata))) == 0) { fprintf(stderr, "%s: buspirate_initpgm(): Out of memory allocating private data\n", progname); exit(1); } }