1 /*
2  * avrdude - A Downloader/Uploader for AVR device programmers
3  * Support for bitbanging GPIO pins using the /sys/class/gpio interface
4  *
5  * Copyright (C) 2013 Radoslav Kolev <radoslav@kolev.info>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #include "ac_cfg.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <errno.h>
30 
31 #include "avrdude.h"
32 #include "libavrdude.h"
33 
34 #include "bitbang.h"
35 
36 #if HAVE_LINUXGPIO
37 
38 /*
39  * GPIO user space helpers
40  *
41  * Copyright 2009 Analog Devices Inc.
42  * Michael Hennerich (hennerich@blackfin.uclinux.org)
43  *
44  * Licensed under the GPL-2 or later
45  */
46 
47 /*
48  * GPIO user space helpers
49  * The following functions are acting on an "unsigned gpio" argument, which corresponds to the
50  * gpio numbering scheme in the kernel (starting from 0).
51  * The higher level functions use "int pin" to specify the pins with an offset of 1:
52  * gpio = pin - 1;
53  */
54 
55 #define GPIO_DIR_IN	0
56 #define GPIO_DIR_OUT	1
57 
linuxgpio_export(unsigned int gpio)58 static int linuxgpio_export(unsigned int gpio)
59 {
60   int fd, len, r;
61   char buf[11];
62 
63   fd = open("/sys/class/gpio/export", O_WRONLY);
64   if (fd < 0) {
65     perror("Can't open /sys/class/gpio/export");
66     return fd;
67   }
68 
69   len = snprintf(buf, sizeof(buf), "%u", gpio);
70   r = write(fd, buf, len);
71   close(fd);
72 
73   return r;
74 }
75 
linuxgpio_unexport(unsigned int gpio)76 static int linuxgpio_unexport(unsigned int gpio)
77 {
78   int fd, len, r;
79   char buf[11];
80 
81   fd = open("/sys/class/gpio/unexport", O_WRONLY);
82   if (fd < 0) {
83     perror("Can't open /sys/class/gpio/unexport");
84     return fd;
85   }
86 
87   len = snprintf(buf, sizeof(buf), "%u", gpio);
88   r = write(fd, buf, len);
89   close(fd);
90 
91   return r;
92 }
93 
linuxgpio_openfd(unsigned int gpio)94 static int linuxgpio_openfd(unsigned int gpio)
95 {
96   char filepath[60];
97 
98   snprintf(filepath, sizeof(filepath), "/sys/class/gpio/gpio%u/value", gpio);
99   return (open(filepath, O_RDWR));
100 }
101 
linuxgpio_dir(unsigned int gpio,unsigned int dir)102 static int linuxgpio_dir(unsigned int gpio, unsigned int dir)
103 {
104   int fd, r;
105   char buf[60];
106 
107   snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%u/direction", gpio);
108 
109   fd = open(buf, O_WRONLY);
110   if (fd < 0) {
111     perror("Can't open gpioX/direction");
112     return fd;
113   }
114 
115   if (dir == GPIO_DIR_OUT)
116     r = write(fd, "out", 4);
117   else
118     r = write(fd, "in", 3);
119 
120   close(fd);
121 
122   return r;
123 }
124 
linuxgpio_dir_out(unsigned int gpio)125 static int linuxgpio_dir_out(unsigned int gpio)
126 {
127   return linuxgpio_dir(gpio, GPIO_DIR_OUT);
128 }
129 
linuxgpio_dir_in(unsigned int gpio)130 static int linuxgpio_dir_in(unsigned int gpio)
131 {
132   return linuxgpio_dir(gpio, GPIO_DIR_IN);
133 }
134 
135 /*
136  * End of GPIO user space helpers
137  */
138 
139 #define N_GPIO (PIN_MAX + 1)
140 
141 /*
142 * an array which holds open FDs to /sys/class/gpio/gpioXX/value for all needed pins
143 */
144 static int linuxgpio_fds[N_GPIO] ;
145 
146 
linuxgpio_setpin(PROGRAMMER * pgm,int pinfunc,int value)147 static int linuxgpio_setpin(PROGRAMMER * pgm, int pinfunc, int value)
148 {
149   int r;
150   int pin = pgm->pinno[pinfunc]; // TODO
151 
152   if (pin & PIN_INVERSE)
153   {
154     value  = !value;
155     pin   &= PIN_MASK;
156   }
157 
158   if ( linuxgpio_fds[pin] < 0 )
159     return -1;
160 
161   if (value)
162     r = write(linuxgpio_fds[pin], "1", 1);
163   else
164     r = write(linuxgpio_fds[pin], "0", 1);
165 
166   if (r!=1) return -1;
167 
168   if (pgm->ispdelay > 1)
169     bitbang_delay(pgm->ispdelay);
170 
171   return 0;
172 }
173 
linuxgpio_getpin(PROGRAMMER * pgm,int pinfunc)174 static int linuxgpio_getpin(PROGRAMMER * pgm, int pinfunc)
175 {
176   unsigned char invert=0;
177   char c;
178   int pin = pgm->pinno[pinfunc]; // TODO
179 
180   if (pin & PIN_INVERSE)
181   {
182     invert = 1;
183     pin   &= PIN_MASK;
184   }
185 
186   if ( linuxgpio_fds[pin] < 0 )
187     return -1;
188 
189   if (lseek(linuxgpio_fds[pin], 0, SEEK_SET)<0)
190     return -1;
191 
192   if (read(linuxgpio_fds[pin], &c, 1)!=1)
193     return -1;
194 
195   if (c=='0')
196     return 0+invert;
197   else if (c=='1')
198     return 1-invert;
199   else
200     return -1;
201 
202 }
203 
linuxgpio_highpulsepin(PROGRAMMER * pgm,int pinfunc)204 static int linuxgpio_highpulsepin(PROGRAMMER * pgm, int pinfunc)
205 {
206   int pin = pgm->pinno[pinfunc]; // TODO
207 
208   if ( linuxgpio_fds[pin & PIN_MASK] < 0 )
209     return -1;
210 
211   linuxgpio_setpin(pgm, pinfunc, 1);
212   linuxgpio_setpin(pgm, pinfunc, 0);
213 
214   return 0;
215 }
216 
217 
218 
linuxgpio_display(PROGRAMMER * pgm,const char * p)219 static void linuxgpio_display(PROGRAMMER *pgm, const char *p)
220 {
221     avrdude_message(MSG_INFO, "%sPin assignment  : /sys/class/gpio/gpio{n}\n",p);
222     pgm_display_generic_mask(pgm, p, SHOW_AVR_PINS);
223 }
224 
linuxgpio_enable(PROGRAMMER * pgm)225 static void linuxgpio_enable(PROGRAMMER *pgm)
226 {
227   /* nothing */
228 }
229 
linuxgpio_disable(PROGRAMMER * pgm)230 static void linuxgpio_disable(PROGRAMMER *pgm)
231 {
232   /* nothing */
233 }
234 
linuxgpio_powerup(PROGRAMMER * pgm)235 static void linuxgpio_powerup(PROGRAMMER *pgm)
236 {
237   /* nothing */
238 }
239 
linuxgpio_powerdown(PROGRAMMER * pgm)240 static void linuxgpio_powerdown(PROGRAMMER *pgm)
241 {
242   /* nothing */
243 }
244 
linuxgpio_open(PROGRAMMER * pgm,char * port)245 static int linuxgpio_open(PROGRAMMER *pgm, char *port)
246 {
247   int r, i, pin;
248 
249   if (bitbang_check_prerequisites(pgm) < 0)
250     return -1;
251 
252 
253   for (i=0; i<N_GPIO; i++)
254     linuxgpio_fds[i] = -1;
255   //Avrdude assumes that if a pin number is 0 it means not used/available
256   //this causes a problem because 0 is a valid GPIO number in Linux sysfs.
257   //To avoid annoying off by one pin numbering we assume SCK, MOSI, MISO
258   //and RESET pins are always defined in avrdude.conf, even as 0. If they're
259   //not programming will not work anyway. The drawbacks of this approach are
260   //that unwanted toggling of GPIO0 can occur and that other optional pins
261   //mostry LED status, can't be set to GPIO0. It can be fixed when a better
262   //solution exists.
263   for (i=0; i<N_PINS; i++) {
264     if ( (pgm->pinno[i] & PIN_MASK) != 0 ||
265          i == PIN_AVR_RESET ||
266          i == PIN_AVR_SCK   ||
267          i == PIN_AVR_MOSI  ||
268          i == PIN_AVR_MISO ) {
269         pin = pgm->pinno[i] & PIN_MASK;
270         if ((r=linuxgpio_export(pin)) < 0) {
271             avrdude_message(MSG_INFO, "Can't export GPIO %d, already exported/busy?: %s",
272                     pin, strerror(errno));
273             return r;
274         }
275         if (i == PIN_AVR_MISO)
276             r=linuxgpio_dir_in(pin);
277         else
278             r=linuxgpio_dir_out(pin);
279 
280         if (r < 0)
281             return r;
282 
283         if ((linuxgpio_fds[pin]=linuxgpio_openfd(pin)) < 0)
284             return linuxgpio_fds[pin];
285     }
286   }
287 
288  return(0);
289 }
290 
linuxgpio_close(PROGRAMMER * pgm)291 static void linuxgpio_close(PROGRAMMER *pgm)
292 {
293   int i, reset_pin;
294 
295   reset_pin = pgm->pinno[PIN_AVR_RESET] & PIN_MASK;
296 
297   //first configure all pins as input, except RESET
298   //this should avoid possible conflicts when AVR firmware starts
299   for (i=0; i<N_GPIO; i++) {
300     if (linuxgpio_fds[i] >= 0 && i != reset_pin) {
301        close(linuxgpio_fds[i]);
302        linuxgpio_dir_in(i);
303        linuxgpio_unexport(i);
304     }
305   }
306   //configure RESET as input, if there's external pull up it will go high
307   if (linuxgpio_fds[reset_pin] >= 0) {
308     close(linuxgpio_fds[reset_pin]);
309     linuxgpio_dir_in(reset_pin);
310     linuxgpio_unexport(reset_pin);
311   }
312 }
313 
linuxgpio_initpgm(PROGRAMMER * pgm)314 void linuxgpio_initpgm(PROGRAMMER *pgm)
315 {
316   strcpy(pgm->type, "linuxgpio");
317 
318   pgm_fill_old_pins(pgm); // TODO to be removed if old pin data no longer needed
319 
320   pgm->rdy_led        = bitbang_rdy_led;
321   pgm->err_led        = bitbang_err_led;
322   pgm->pgm_led        = bitbang_pgm_led;
323   pgm->vfy_led        = bitbang_vfy_led;
324   pgm->initialize     = bitbang_initialize;
325   pgm->display        = linuxgpio_display;
326   pgm->enable         = linuxgpio_enable;
327   pgm->disable        = linuxgpio_disable;
328   pgm->powerup        = linuxgpio_powerup;
329   pgm->powerdown      = linuxgpio_powerdown;
330   pgm->program_enable = bitbang_program_enable;
331   pgm->chip_erase     = bitbang_chip_erase;
332   pgm->cmd            = bitbang_cmd;
333   pgm->open           = linuxgpio_open;
334   pgm->close          = linuxgpio_close;
335   pgm->setpin         = linuxgpio_setpin;
336   pgm->getpin         = linuxgpio_getpin;
337   pgm->highpulsepin   = linuxgpio_highpulsepin;
338   pgm->read_byte      = avr_read_byte_default;
339   pgm->write_byte     = avr_write_byte_default;
340 }
341 
342 const char linuxgpio_desc[] = "GPIO bitbanging using the Linux sysfs interface";
343 
344 #else  /* !HAVE_LINUXGPIO */
345 
linuxgpio_initpgm(PROGRAMMER * pgm)346 void linuxgpio_initpgm(PROGRAMMER * pgm)
347 {
348   avrdude_message(MSG_INFO, "%s: Linux sysfs GPIO support not available in this configuration\n",
349                   progname);
350 }
351 
352 const char linuxgpio_desc[] = "GPIO bitbanging using the Linux sysfs interface (not available)";
353 
354 #endif /* HAVE_LINUXGPIO */
355