1 /*
2  * avrdude - A Downloader/Uploader for AVR device programmers
3  * Support for using spidev userspace drivers to communicate directly over SPI
4  *
5  * Copyright (C) 2013 Kevin Cuzner <kevin@kevincuzner.com>
6  * Copyright (C) 2018 Ralf Ramsauer <ralf@vmexit.de>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * Support for inversion of reset pin, Tim Chilton 02/05/2014
23  * Review code, rebase to latest trunk, add linux/gpio.h support, Ralf Ramsauer 2018-09-07
24  */
25 
26 
27 #include "ac_cfg.h"
28 
29 #include "avrdude.h"
30 #include "libavrdude.h"
31 
32 #include "linuxspi.h"
33 
34 #if HAVE_LINUXSPI
35 
36 /**
37  * Linux Kernel SPI Drivers
38  *
39  * Copyright (C) 2006 SWAPP
40  *      Andrea Paterniani <a.paterniani@swapp-eng.it>
41  * Copyright (C) 2007 David Brownell (simplification, cleanup)
42  *
43  * This program is free software; you can redistribute it and/or modify
44  * it under the terms of the GNU General Public License as published by
45  * the Free Software Foundation; either version 2 of the License, or
46  * (at your option) any later version.
47  */
48 
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <unistd.h>
52 #include <sys/ioctl.h>
53 #include <linux/types.h>
54 #include <linux/spi/spidev.h>
55 #include <linux/gpio.h>
56 
57 #include <stddef.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <math.h>
62 
63 #define LINUXSPI "linuxspi"
64 
65 static int fd_spidev, fd_gpiochip, fd_linehandle;
66 
67 /**
68  * @brief Sends/receives a message in full duplex mode
69  * @return -1 on failure, otherwise number of bytes sent/recieved
70  */
linuxspi_spi_duplex(PROGRAMMER * pgm,const unsigned char * tx,unsigned char * rx,int len)71 static int linuxspi_spi_duplex(PROGRAMMER *pgm, const unsigned char *tx, unsigned char *rx, int len)
72 {
73     struct spi_ioc_transfer tr;
74     int ret;
75 
76     tr = (struct spi_ioc_transfer) {
77         .tx_buf = (unsigned long)tx,
78         .rx_buf = (unsigned long)rx,
79         .len = len,
80         .delay_usecs = 1,
81 	.speed_hz = 1.0 / pgm->bitclock, // seconds to Hz
82         .bits_per_word = 8,
83     };
84 
85     ret = ioctl(fd_spidev, SPI_IOC_MESSAGE(1), &tr);
86     if (ret != len)
87         avrdude_message(MSG_INFO, "\n%s: error: Unable to send SPI message\n", progname);
88 
89     return (ret == -1) ? -1 : 0;
90 }
91 
linuxspi_setup(PROGRAMMER * pgm)92 static void linuxspi_setup(PROGRAMMER *pgm)
93 {
94 }
95 
linuxspi_teardown(PROGRAMMER * pgm)96 static void linuxspi_teardown(PROGRAMMER* pgm)
97 {
98 }
99 
linuxspi_reset_mcu(PROGRAMMER * pgm,bool active)100 static int linuxspi_reset_mcu(PROGRAMMER *pgm, bool active)
101 {
102     struct gpiohandle_data data;
103     int ret;
104 
105     /*
106      * Set the reset state and keep it. The pin will be released and set back to
107      * its initial value, once the fd_gpiochip is closed.
108      */
109     data.values[0] = active ^ !(pgm->pinno[PIN_AVR_RESET] & PIN_INVERSE);
110     ret = ioctl(fd_linehandle, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
111 #ifdef GPIO_V2_LINE_SET_VALUES_IOCTL
112     if (ret == -1) {
113         struct gpio_v2_line_values val;
114 
115         val.mask = 1;
116         val.bits = active ^ !(pgm->pinno[PIN_AVR_RESET] & PIN_INVERSE);
117 
118         ret = ioctl(fd_linehandle, GPIO_V2_LINE_SET_VALUES_IOCTL, &val);
119     }
120 #endif
121     if (ret == -1) {
122         ret = -errno;
123         avrdude_message(MSG_INFO, "%s error: Unable to set GPIO line %d value\n",
124                         progname, pgm->pinno[PIN_AVR_RESET] & ~PIN_INVERSE);
125         return ret;
126     }
127 
128     return 0;
129 }
130 
linuxspi_open(PROGRAMMER * pgm,char * port)131 static int linuxspi_open(PROGRAMMER *pgm, char *port)
132 {
133     const char *port_error =
134       "%s: error: Unknown port specification. "
135       "Please use the format /dev/spidev:/dev/gpiochip[:resetno]\n";
136     char port_default[] = "/dev/spidev0.0:/dev/gpiochip0";
137     char *spidev, *gpiochip, *reset_pin;
138     struct gpiohandle_request req;
139     int ret;
140 
141     if (!strcmp(port, "unknown")) {
142         port = port_default;
143     }
144 
145     spidev = strtok(port, ":");
146     if (!spidev) {
147         avrdude_message(MSG_INFO, port_error, progname);
148         return -1;
149     }
150 
151     gpiochip = strtok(NULL, ":");
152     if (!gpiochip) {
153         avrdude_message(MSG_INFO, port_error, progname);
154         return -1;
155     }
156 
157     /* optional: override reset pin in configuration */
158     reset_pin = strtok(NULL, ":");
159     if (reset_pin)
160         pgm->pinno[PIN_AVR_RESET] = strtoul(reset_pin, NULL, 0);
161 
162     strcpy(pgm->port, port);
163     fd_spidev = open(pgm->port, O_RDWR);
164     if (fd_spidev < 0) {
165         avrdude_message(MSG_INFO, "\n%s: error: Unable to open the spidev device %s", progname, pgm->port);
166         return -1;
167     }
168 
169     uint32_t mode = SPI_MODE_0 | SPI_NO_CS;
170     ret = ioctl(fd_spidev, SPI_IOC_WR_MODE32, &mode);
171     if (ret == -1) {
172         avrdude_message(MSG_INFO, "%s: error: Unable to set SPI mode %0X on %s\n",
173                         progname, mode, spidev);
174         goto close_spidev;
175     }
176     fd_gpiochip = open(gpiochip, 0);
177     if (fd_gpiochip < 0) {
178         avrdude_message(MSG_INFO, "\n%s error: Unable to open the gpiochip %s", progname, gpiochip);
179         ret = -1;
180         goto close_spidev;
181     }
182 
183     strcpy(req.consumer_label, progname);
184     req.lines = 1;
185     req.lineoffsets[0] = pgm->pinno[PIN_AVR_RESET] & ~PIN_INVERSE;
186     req.default_values[0] = !!(pgm->pinno[PIN_AVR_RESET] & PIN_INVERSE);
187     req.flags = GPIOHANDLE_REQUEST_OUTPUT;
188 
189     ret = ioctl(fd_gpiochip, GPIO_GET_LINEHANDLE_IOCTL, &req);
190     if (ret != -1)
191         fd_linehandle = req.fd;
192 #ifdef GPIO_V2_GET_LINE_IOCTL
193     if (ret == -1) {
194         struct gpio_v2_line_request reqv2;
195 
196         memset(&reqv2, 0, sizeof(reqv2));
197         reqv2.offsets[0] = pgm->pinno[PIN_AVR_RESET] & ~PIN_INVERSE;
198         strncpy(reqv2.consumer, progname, sizeof(reqv2.consumer) - 1);
199         reqv2.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
200         reqv2.config.num_attrs = 1;
201         reqv2.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
202         reqv2.config.attrs[0].attr.values = !!(pgm->pinno[PIN_AVR_RESET] & PIN_INVERSE);
203         reqv2.config.attrs[0].mask = 1;
204         reqv2.num_lines = 1;
205 
206         ret = ioctl(fd_gpiochip, GPIO_V2_GET_LINE_IOCTL, &reqv2);
207         if (ret != -1)
208             fd_linehandle = reqv2.fd;
209     }
210 #endif
211     if (ret == -1) {
212         ret = -errno;
213         avrdude_message(MSG_INFO, "%s error: Unable to get GPIO line %d\n",
214                         progname, pgm->pinno[PIN_AVR_RESET] & ~PIN_INVERSE);
215         goto close_gpiochip;
216     }
217 
218     ret = linuxspi_reset_mcu(pgm, true);
219     if (ret)
220         goto close_out;
221 
222     if (pgm->baudrate != 0) {
223       avrdude_message(MSG_INFO,
224 		      "%s: obsolete use of -b <clock> option for bit clock; use -B <clock>\n",
225 		      progname);
226       pgm->bitclock = 1E6 / pgm->baudrate;
227     }
228     if (pgm->bitclock == 0) {
229       avrdude_message(MSG_NOTICE,
230 		      "%s: defaulting bit clock to 200 kHz\n",
231 		      progname);
232       pgm->bitclock = 5E-6; // 200 kHz - 5 µs
233     }
234 
235     return 0;
236 
237 close_out:
238     close(fd_linehandle);
239 close_gpiochip:
240     close(fd_gpiochip);
241 close_spidev:
242     close(fd_spidev);
243     return ret;
244 }
245 
linuxspi_close(PROGRAMMER * pgm)246 static void linuxspi_close(PROGRAMMER *pgm)
247 {
248     switch (pgm->exit_reset) {
249     case EXIT_RESET_ENABLED:
250         linuxspi_reset_mcu(pgm, true);
251         break;
252 
253     case EXIT_RESET_DISABLED:
254         linuxspi_reset_mcu(pgm, false);
255         break;
256 
257     default:
258         break;
259     }
260 
261     close(fd_linehandle);
262     close(fd_spidev);
263     close(fd_gpiochip);
264 }
265 
linuxspi_disable(PROGRAMMER * pgm)266 static void linuxspi_disable(PROGRAMMER* pgm)
267 {
268 }
269 
linuxspi_enable(PROGRAMMER * pgm)270 static void linuxspi_enable(PROGRAMMER* pgm)
271 {
272 }
273 
linuxspi_display(PROGRAMMER * pgm,const char * p)274 static void linuxspi_display(PROGRAMMER* pgm, const char* p)
275 {
276 }
277 
linuxspi_initialize(PROGRAMMER * pgm,AVRPART * p)278 static int linuxspi_initialize(PROGRAMMER *pgm, AVRPART *p)
279 {
280     int tries, ret;
281 
282     if (p->flags & AVRPART_HAS_TPI) {
283         /* We do not support tpi. This is a dedicated SPI thing */
284         avrdude_message(MSG_INFO, "%s: error: Programmer " LINUXSPI " does not support TPI\n", progname);
285         return -1;
286     }
287 
288     //enable programming on the part
289     tries = 0;
290     do
291     {
292         ret = pgm->program_enable(pgm, p);
293         if (ret == 0 || ret == -1)
294             break;
295     } while(tries++ < 65);
296 
297     if (ret)
298         avrdude_message(MSG_INFO, "%s: error: AVR device not responding\n", progname);
299 
300     return ret;
301 }
302 
linuxspi_cmd(PROGRAMMER * pgm,const unsigned char * cmd,unsigned char * res)303 static int linuxspi_cmd(PROGRAMMER *pgm, const unsigned char *cmd, unsigned char *res)
304 {
305     return linuxspi_spi_duplex(pgm, cmd, res, 4);
306 }
307 
linuxspi_program_enable(PROGRAMMER * pgm,AVRPART * p)308 static int linuxspi_program_enable(PROGRAMMER *pgm, AVRPART *p)
309 {
310     unsigned char cmd[4], res[4];
311 
312     if (!p->op[AVR_OP_PGM_ENABLE]) {
313         avrdude_message(MSG_INFO, "%s: error: program enable instruction not defined for part \"%s\"\n", progname, p->desc);
314         return -1;
315     }
316 
317     memset(cmd, 0, sizeof(cmd));
318     avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd); //set the cmd
319     pgm->cmd(pgm, cmd, res);
320 
321     if (res[2] != cmd[1]) {
322         /*
323          * From ATtiny441 datasheet:
324          *
325          * In some systems, the programmer can not guarantee that SCK is held low
326          * during power-up. In this case, RESET must be given a positive pulse after
327          * SCK has been set to '0'. The duration of the pulse must be at least t RST
328          * plus two CPU clock cycles. See Table 25-5 on page 240 for definition of
329          * minimum pulse width on RESET pin, t RST
330          * 2. Wait for at least 20 ms and then enable serial programming by sending
331          * the Programming Enable serial instruction to the MOSI pin
332          * 3. The serial programming instructions will not work if the communication
333          * is out of synchronization. When in sync, the second byte (0x53) will echo
334          * back when issuing the third byte of the Programming Enable instruction
335          * ...
336          * If the 0x53 did not echo back, give RESET a positive pulse and issue a
337          * new Programming Enable command
338          */
339         if (linuxspi_reset_mcu(pgm, false))
340             return -1;
341         usleep(5);
342         if (linuxspi_reset_mcu(pgm, true))
343             return -1;
344         usleep(20000);
345 
346         return -2;
347     }
348 
349     return 0;
350 }
351 
linuxspi_chip_erase(PROGRAMMER * pgm,AVRPART * p)352 static int linuxspi_chip_erase(PROGRAMMER *pgm, AVRPART *p)
353 {
354     unsigned char cmd[4], res[4];
355 
356     if (!p->op[AVR_OP_CHIP_ERASE]) {
357         avrdude_message(MSG_INFO, "%s: error: chip erase instruction not defined for part \"%s\"\n", progname, p->desc);
358         return -1;
359     }
360 
361     memset(cmd, 0, sizeof(cmd));
362     avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
363     pgm->cmd(pgm, cmd, res);
364     usleep(p->chip_erase_delay);
365     pgm->initialize(pgm, p);
366 
367     return 0;
368 }
369 
linuxspi_parseexitspecs(PROGRAMMER * pgm,char * s)370 static int linuxspi_parseexitspecs(PROGRAMMER *pgm, char *s)
371 {
372     char *cp;
373 
374     while ((cp = strtok(s, ","))) {
375         s = 0;
376         if (!strcmp(cp, "reset")) {
377             pgm->exit_reset = EXIT_RESET_ENABLED;
378             continue;
379         }
380         if (!strcmp(cp, "noreset")) {
381             pgm->exit_reset = EXIT_RESET_DISABLED;
382             continue;
383         }
384         return -1;
385     }
386 
387     return 0;
388 }
389 
linuxspi_initpgm(PROGRAMMER * pgm)390 void linuxspi_initpgm(PROGRAMMER *pgm)
391 {
392     strcpy(pgm->type, LINUXSPI);
393 
394     pgm_fill_old_pins(pgm); // TODO to be removed if old pin data no longer needed
395 
396     /* mandatory functions */
397     pgm->initialize     = linuxspi_initialize;
398     pgm->display        = linuxspi_display;
399     pgm->enable         = linuxspi_enable;
400     pgm->disable        = linuxspi_disable;
401     pgm->program_enable = linuxspi_program_enable;
402     pgm->chip_erase     = linuxspi_chip_erase;
403     pgm->cmd            = linuxspi_cmd;
404     pgm->open           = linuxspi_open;
405     pgm->close          = linuxspi_close;
406     pgm->read_byte      = avr_read_byte_default;
407     pgm->write_byte     = avr_write_byte_default;
408 
409     /* optional functions */
410     pgm->setup          = linuxspi_setup;
411     pgm->teardown       = linuxspi_teardown;
412     pgm->parseexitspecs = linuxspi_parseexitspecs;
413 }
414 
415 const char linuxspi_desc[] = "SPI using Linux spidev driver";
416 
417 #else /* !HAVE_LINUXSPI */
418 
linuxspi_initpgm(PROGRAMMER * pgm)419 void linuxspi_initpgm(PROGRAMMER * pgm)
420 {
421     avrdude_message(MSG_INFO, "%s: Linux SPI driver not available in this configuration\n",
422                     progname);
423 }
424 
425 const char linuxspi_desc[] = "SPI using Linux spidev driver (not available)";
426 
427 #endif /* HAVE_LINUXSPI */
428