diff --git a/ChangeLog b/ChangeLog index d55eef16..a25111e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2013-01-09 Rene Liebscher + + patch #7165 Add support for bitbanging GPIO lines using the Linux sysf GPIO interface + * doc/avrdude.texi,avrdude.1: added doc for linuxgpio + * avrdude.conf.in: added template for linuxgpio programmer + * config_gram.y: pin numbers restricted to [PIN_MIN, PIN_MAX] + * pindefs.h: added PIN_MIN, PIN_MAX, removed unused LED_ON/OFF + * configure.ac: configure option enable-linuxgpio, print of enabled options + * linuxgpio.[ch]: new source for linuxgpio programmer + * Makefile.am: added linuxgpio to sources list + * pgm_type.c: added linuxgpio to programmer types list + 2013-01-08 Joerg Wunsch * jtagmkI.c (jtagmkI_prmsg): replace a putchar() by putc(...stderr) diff --git a/Makefile.am b/Makefile.am index 0c06de0e..46914bab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,6 +31,7 @@ EXTRA_DIST = \ ChangeLog-2009 \ ChangeLog-2010 \ ChangeLog-2011 \ + ChangeLog-2012 \ avrdude.1 \ avrdude.spec \ bootstrap @@ -123,6 +124,8 @@ libavrdude_a_SOURCES = \ jtag3.c \ jtag3.h \ jtag3_private.h \ + linuxgpio.c \ + linuxgpio.h \ linux_ppdev.h \ lists.c \ lists.h \ diff --git a/NEWS b/NEWS index 3ba599b9..dad6b75e 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,10 @@ Current: - ATmega256RFR2, ATmega128RFR2, ATmega64RFR2 * New programmers supported: + - linuxgpio + + any (embedded) Linux system with 4 GPIOs available can be used + as a programmer with little or no additional hardware. + - avrftdi + o-link (patch #7672 adding support for O-Link (FTDI based JTAG) as programmer) diff --git a/avrdude.1 b/avrdude.1 index 291fd112..8fe1ae5f 100644 --- a/avrdude.1 +++ b/avrdude.1 @@ -98,6 +98,18 @@ port. Connecting to a serial port emulated on top of USB is likely to not work at all, or to work abysmally slow. .Pp +If you happen to have a Linux system with at least 4 hardware GPIOs +available (like almost all embedded Linux boards) you can do without +any additional hardware - just connect them to the MOSI, MISO, RESET +and SCK pins on the AVR and use the linuxgpio programmer type. It bitbangs +the lines using the Linux sysfs GPIO interface. Of course, care should +be taken about voltage level compatibility. Also, although not strictrly +required, it is strongly advisable to protect the GPIO pins from +overcurrent situations in some way. The simplest would be to just put +some resistors in series or better yet use a 3-state buffer driver like +the 74HC244. Have a look at http://kolev.info/avrdude-linuxgpio for a more +detailed tutorial about using this programmer type. +.Pp Atmel's STK500 programmer is also supported and connects to a serial port. Both, firmware versions 1.x and 2.x can be handled, but require a diff --git a/avrdude.conf.in b/avrdude.conf.in index 6aad0df5..b4c4b36a 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -1142,7 +1142,28 @@ programmer @HAVE_PARPORT_END@ +#This programmer bitbangs GPIO lines using the Linux sysfs GPIO interface # +#To enable it set the configuration below to match the GPIO lines connected to the +#relevant ISP header pins and uncomment the entry definition. In case you don't +#have the required permissions to edit this system wide config file put the +#entry in a separate .conf file and use it with -C+.conf +#on the command line. +# +#To check if your avrdude build has support for the linuxgpio programmer compiled in, +#use -c?type on the command line and look for linuxgpio in the list. If it's not available +#you need pass the --enable-linuxgpio=yes option to configure and recompile avrdude. +# +#programmer +# id = "linuxgpio"; +# desc = "Use the Linux sysfs interface to bitbang GPIO lines"; +# type = "linuxgpio"; +# reset = ?; +# sck = ?; +# mosi = ?; +# miso = ?; +#; + # some ultra cheap programmers use bitbanging on the # serialport. # diff --git a/config_gram.y b/config_gram.y index 3929d631..02deb57e 100644 --- a/config_gram.y +++ b/config_gram.y @@ -1346,11 +1346,11 @@ static int assign_pin(int pinno, TOKEN * v, int invert) value = v->value.number; free_token(v); - if ((value <= 0) || (value >= 18)) { + if ((value < PIN_MIN) || (value > PIN_MAX)) { fprintf(stderr, "%s: error at line %d of %s: pin must be in the " - "range 1-17\n", - progname, lineno, infile); + "range %d-%d\n", + progname, lineno, infile, PIN_MIN, PIN_MAX); exit(1); } if (invert) diff --git a/configure.ac b/configure.ac index 260f8d24..6e688c59 100644 --- a/configure.ac +++ b/configure.ac @@ -271,6 +271,18 @@ AC_ARG_ENABLE( *) AC_MSG_ERROR(bad value ${enableval} for enable-parport option) ;; esac], [enabled_parport=yes]) + +AC_ARG_ENABLE( + [linuxgpio], + AC_HELP_STRING( + [--enable-linuxgpio], + [Enable the Linux sysfs GPIO interface programmer type]), + [case "${enableval}" in + yes) enabled_linuxgpio=yes ;; + no) enabled_linuxgpio=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-linuxgpio option) ;; + esac], + [enabled_linuxgpio=no]) DIST_SUBDIRS_AC='doc windows' @@ -340,6 +352,15 @@ else confsubst="-e /^@HAVE_PARPORT_BEGIN@/,/^@HAVE_PARPORT_END@/d" fi + +if test "$enabled_linuxgpio" = "yes"; then + AC_DEFINE(HAVE_LINUXGPIO, 1, [Linux sysfs GPIO support enabled]) + confsubst="$confsubst -e /^@HAVE_LINUXGPIO_/d" +else + confsubst="$confsubst -e /^@HAVE_LINUXGPIO_BEGIN@/,/^@HAVE_LINUXGPIO_END@/d" +fi + + # If we are compiling with gcc, enable all warning and make warnings errors. if test "$GCC" = yes; then ENABLE_WARNINGS="-Wall" @@ -479,3 +500,21 @@ else echo "DON'T HAVE pthread" fi +if test x$enabled_doc = xyes; then + echo "ENABLED doc" +else + echo "DISABLED doc" +fi + +if test x$enabled_parport = xyes; then + echo "ENABLED parport" +else + echo "DISABLED parport" +fi + +if test x$enabled_linuxgpio = xyes; then + echo "ENABLED linuxgpio" +else + echo "DISABLED linuxgpio" +fi + diff --git a/doc/avrdude.texi b/doc/avrdude.texi index f4725ecc..e28c50ae 100644 --- a/doc/avrdude.texi +++ b/doc/avrdude.texi @@ -168,6 +168,18 @@ attached to a physical serial port. Connecting to a serial port emulated on top of USB is likely to not work at all, or to work abysmally slow. +If you happen to have a Linux system with at least 4 hardware GPIOs +available (like almost all embedded Linux boards) you can do without +any additional hardware - just connect them to the MOSI, MISO, RESET +and SCK pins on the AVR and use the linuxgpio programmer type. It bitbangs +the lines using the Linux sysfs GPIO interface. Of course, care should +be taken about voltage level compatibility. Also, although not strictrly +required, it is strongly advisable to protect the GPIO pins from +overcurrent situations in some way. The simplest would be to just put +some resistors in series or better yet use a 3-state buffer driver like +the 74HC244. Have a look at http://kolev.info/avrdude-linuxgpio for a more +detailed tutorial about using this programmer type. + The STK500, JTAG ICE, avr910, and avr109/butterfly use the serial port to communicate with the PC. The STK600, JTAG ICE mkII, AVRISP mkII, USBasp, avrftdi (and derivitives), and USBtinyISP programmers communicate through the USB, using @code{libusb} as a diff --git a/linuxgpio.c b/linuxgpio.c new file mode 100644 index 00000000..ed464f6b --- /dev/null +++ b/linuxgpio.c @@ -0,0 +1,349 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Support for bitbanging GPIO pins using the /sys/class/gpio interface + * + * Copyright (C) 2013 Radoslav Kolev + * + * 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 + */ + +#include "ac_cfg.h" + +#include +#include +#include +#include +#include +#include + +#include "avrdude.h" +#include "avr.h" +#include "pindefs.h" +#include "pgm.h" +#include "bitbang.h" + +#if HAVE_LINUXGPIO + +/* + * GPIO user space helpers + * + * Copyright 2009 Analog Devices Inc. + * Michael Hennerich (hennerich@blackfin.uclinux.org) + * + * Licensed under the GPL-2 or later + */ + +/* + * GPIO user space helpers + * The following functions are acting on an "unsigned gpio" argument, which corresponds to the + * gpio numbering scheme in the kernel (starting from 0). + * The higher level functions use "int pin" to specify the pins with an offset of 1: + * gpio = pin - 1; + */ + +#define GPIO_DIR_IN 0 +#define GPIO_DIR_OUT 1 + +static int linuxgpio_export(unsigned int gpio) +{ + int fd, len, r; + char buf[11]; + + fd = open("/sys/class/gpio/export", O_WRONLY); + if (fd < 0) { + perror("Can't open /sys/class/gpio/export"); + return fd; + } + + len = snprintf(buf, sizeof(buf), "%d", gpio); + r = write(fd, buf, len); + close(fd); + + return r; +} + +static int linuxgpio_unexport(unsigned int gpio) +{ + int fd, len, r; + char buf[11]; + + fd = open("/sys/class/gpio/unexport", O_WRONLY); + if (fd < 0) { + perror("Can't open /sys/class/gpio/unexport"); + return fd; + } + + len = snprintf(buf, sizeof(buf), "%d", gpio); + r = write(fd, buf, len); + close(fd); + + return r; +} + +static int linuxgpio_openfd(unsigned int gpio) +{ + char filepath[60]; + + snprintf(filepath, sizeof(filepath), "/sys/class/gpio/gpio%d/value", gpio); + return (open(filepath, O_RDWR)); +} + +static int linuxgpio_dir(unsigned int gpio, unsigned int dir) +{ + int fd, r; + char buf[60]; + + snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio); + + fd = open(buf, O_WRONLY); + if (fd < 0) { + perror("Can't open gpioX/direction"); + return fd; + } + + if (dir == GPIO_DIR_OUT) + r = write(fd, "out", 4); + else + r = write(fd, "in", 3); + + close(fd); + + return r; +} + +static int linuxgpio_dir_out(unsigned int gpio) +{ + return linuxgpio_dir(gpio, GPIO_DIR_OUT); +} + +static int linuxgpio_dir_in(unsigned int gpio) +{ + return linuxgpio_dir(gpio, GPIO_DIR_IN); +} + +/* + * End of GPIO user space helpers + */ + +#define N_GPIO (PIN_MAX + 1) + +/* +* an array which holds open FDs to /sys/class/gpio/gpioXX/value for all needed pins +*/ +static int linuxgpio_fds[N_GPIO] ; + + +static int linuxgpio_setpin(PROGRAMMER * pgm, int pin, int value) +{ + int r; + + if (pin & PIN_INVERSE) + { + value = !value; + pin &= PIN_MASK; + } + + if ( linuxgpio_fds[pin] < 0 ) + return -1; + + if (value) + r = write(linuxgpio_fds[pin], "1", 1); + else + r = write(linuxgpio_fds[pin], "0", 1); + + if (r!=1) return -1; + + if (pgm->ispdelay > 1) + bitbang_delay(pgm->ispdelay); + + return 0; +} + +static int linuxgpio_getpin(PROGRAMMER * pgm, int pin) +{ + unsigned char invert=0; + char c; + + if (pin & PIN_INVERSE) + { + invert = 1; + pin &= PIN_MASK; + } + + if ( linuxgpio_fds[pin] < 0 ) + return -1; + + if (lseek(linuxgpio_fds[pin], 0, SEEK_SET)<0) + return -1; + + if (read(linuxgpio_fds[pin], &c, 1)!=1) + return -1; + + if (c=='0') + return 0+invert; + else if (c=='1') + return 1-invert; + else + return -1; + +} + +static int linuxgpio_highpulsepin(PROGRAMMER * pgm, int pin) +{ + + if ( linuxgpio_fds[pin & PIN_MASK] < 0 ) + return -1; + + linuxgpio_setpin(pgm, pin, 1); + linuxgpio_setpin(pgm, pin, 0); + + return 0; +} + + + +static void linuxgpio_display(PROGRAMMER *pgm, const char *p) +{ + /* MAYBE */ +} + +static void linuxgpio_enable(PROGRAMMER *pgm) +{ + /* nothing */ +} + +static void linuxgpio_disable(PROGRAMMER *pgm) +{ + /* nothing */ +} + +static void linuxgpio_powerup(PROGRAMMER *pgm) +{ + /* nothing */ +} + +static void linuxgpio_powerdown(PROGRAMMER *pgm) +{ + /* nothing */ +} + +static int linuxgpio_open(PROGRAMMER *pgm, char *port) +{ + int r, i, pin; + + bitbang_check_prerequisites(pgm); + + + for (i=0; ipinno[i] != 0 || + i == PIN_AVR_RESET || + i == PIN_AVR_SCK || + i == PIN_AVR_MOSI || + i == PIN_AVR_MISO ) { + pin = pgm->pinno[i] & PIN_MASK; + if ((r=linuxgpio_export(pin)) < 0) { + fprintf(stderr, "Can't export GPIO %d, already exported/busy?: %s", + pin, strerror(errno)); + return r; + } + if (i == PIN_AVR_MISO) + r=linuxgpio_dir_in(pin); + else + r=linuxgpio_dir_out(pin); + + if (r < 0) + return r; + + if ((linuxgpio_fds[pin]=linuxgpio_openfd(pin)) < 0) + return linuxgpio_fds[pin]; + } + } + + return(0); +} + +static void linuxgpio_close(PROGRAMMER *pgm) +{ + int i, reset_pin; + + reset_pin = pgm->pinno[PIN_AVR_RESET] & PIN_MASK; + + //first configure all pins as input, except RESET + //this should avoid possible conflicts when AVR firmware starts + for (i=0; i= 0 && i != reset_pin) { + close(linuxgpio_fds[i]); + linuxgpio_dir_in(i); + linuxgpio_unexport(i); + } + } + //configure RESET as input, if there's external pull up it will go high + if (linuxgpio_fds[reset_pin] >= 0) { + close(linuxgpio_fds[reset_pin]); + linuxgpio_dir_in(reset_pin); + linuxgpio_unexport(reset_pin); + } +} + +void linuxgpio_initpgm(PROGRAMMER *pgm) +{ + strcpy(pgm->type, "linuxgpio"); + + pgm->rdy_led = bitbang_rdy_led; + pgm->err_led = bitbang_err_led; + pgm->pgm_led = bitbang_pgm_led; + pgm->vfy_led = bitbang_vfy_led; + pgm->initialize = bitbang_initialize; + pgm->display = linuxgpio_display; + pgm->enable = linuxgpio_enable; + pgm->disable = linuxgpio_disable; + pgm->powerup = linuxgpio_powerup; + pgm->powerdown = linuxgpio_powerdown; + pgm->program_enable = bitbang_program_enable; + pgm->chip_erase = bitbang_chip_erase; + pgm->cmd = bitbang_cmd; + pgm->open = linuxgpio_open; + pgm->close = linuxgpio_close; + pgm->setpin = linuxgpio_setpin; + pgm->getpin = linuxgpio_getpin; + pgm->highpulsepin = linuxgpio_highpulsepin; + pgm->read_byte = avr_read_byte_default; + pgm->write_byte = avr_write_byte_default; +} + +const char linuxgpio_desc[] = "GPIO bitbanging using the Linux sysfs interface"; + +#else /* !HAVE_LINUXGPIO */ + +void linuxgpio_initpgm(PROGRAMMER * pgm) +{ + fprintf(stderr, + "%s: Linux sysfs GPIO support not available in this configuration\n", + progname); +} + +const char linuxgpio_desc[] = "GPIO bitbanging using the Linux sysfs interface (not available)"; + +#endif /* HAVE_LINUXGPIO */ diff --git a/linuxgpio.h b/linuxgpio.h new file mode 100644 index 00000000..dc477982 --- /dev/null +++ b/linuxgpio.h @@ -0,0 +1,36 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2013 Radoslav Kolev + * + * 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 + */ + +/* $Id: par.h 722 2007-01-24 22:43:46Z joerg_wunsch $ */ + +#ifndef linuxgpio_h +#define linuxgpio_h + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char linuxgpio_desc[]; +void linuxgpio_initpgm (PROGRAMMER * pgm); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pgm_type.c b/pgm_type.c index b172b1f4..82e97695 100644 --- a/pgm_type.c +++ b/pgm_type.c @@ -38,6 +38,7 @@ #include "jtagmkI.h" #include "jtagmkII.h" #include "jtag3.h" +#include "linuxgpio.h" #include "par.h" #include "pickit2.h" #include "ppi.h" @@ -74,6 +75,7 @@ const PROGRAMMER_TYPE const programmers_types[] = { {"jtagice3_pdi", jtag3_pdi_initpgm, jtag3_pdi_desc}, {"jtagice3_dw", jtag3_dw_initpgm, jtag3_dw_desc}, {"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc}, + {"linuxgpio", linuxgpio_initpgm, linuxgpio_desc}, {"par", par_initpgm, par_desc}, {"pickit2", pickit2_initpgm, pickit2_desc}, {"serbb", serbb_initpgm, serbb_desc}, diff --git a/pindefs.h b/pindefs.h index ea74cbbf..884ce0ff 100644 --- a/pindefs.h +++ b/pindefs.h @@ -38,8 +38,7 @@ enum { }; #define PIN_MASK (UINT_MAX>>1) #define PIN_INVERSE (~(PIN_MASK)) /* flag for inverted pin in serbb */ - -#define LED_ON(fd,pin) ppi_setpin(fd,pin,0) -#define LED_OFF(fd,pin) ppi_setpin(fd,pin,1) +#define PIN_MIN 1 /* smallest allowed pin number */ +#define PIN_MAX 255 /* largest allowed pin number */ #endif