Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/avrdude/usbtiny.c')
-rw-r--r--src/avrdude/usbtiny.c589
1 files changed, 589 insertions, 0 deletions
diff --git a/src/avrdude/usbtiny.c b/src/avrdude/usbtiny.c
new file mode 100644
index 000000000..b6bc7cc07
--- /dev/null
+++ b/src/avrdude/usbtiny.c
@@ -0,0 +1,589 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2007 Dick Streefland, adapted for 5.4 by Limor Fried
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Driver for "usbtiny"-type programmers
+ * Please see http://www.xs4all.nl/~dicks/avr/usbtiny/
+ * and http://www.ladyada.net/make/usbtinyisp/
+ * For example schematics and detailed documentation
+ */
+
+#include "ac_cfg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "avrdude.h"
+#include "libavrdude.h"
+
+#include "usbtiny.h"
+#include "usbdevs.h"
+
+#if defined(HAVE_LIBUSB) // we use LIBUSB to talk to the board
+#if defined(HAVE_USB_H)
+# include <usb.h>
+#elif defined(HAVE_LUSB0_USB_H)
+# include <lusb0_usb.h>
+#else
+# error "libusb needs either <usb.h> or <lusb0_usb.h>"
+#endif
+
+#ifndef HAVE_UINT_T
+typedef unsigned int uint_t;
+#endif
+#ifndef HAVE_ULONG_T
+typedef unsigned long ulong_t;
+#endif
+
+extern int avr_write_byte_default ( PROGRAMMER* pgm, AVRPART* p,
+ AVRMEM* mem, ulong_t addr,
+ unsigned char data );
+/*
+ * Private data for this programmer.
+ */
+struct pdata
+{
+ usb_dev_handle *usb_handle;
+ int sck_period;
+ int chunk_size;
+ int retries;
+};
+
+#define PDATA(pgm) ((struct pdata *)(pgm->cookie))
+
+// ----------------------------------------------------------------------
+
+static void usbtiny_setup(PROGRAMMER * pgm)
+{
+ if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
+ avrdude_message(MSG_INFO, "%s: usbtiny_setup(): Out of memory allocating private data\n",
+ progname);
+ exit(1);
+ }
+ memset(pgm->cookie, 0, sizeof(struct pdata));
+}
+
+static void usbtiny_teardown(PROGRAMMER * pgm)
+{
+ free(pgm->cookie);
+}
+
+// Wrapper for simple usb_control_msg messages
+static int usb_control (PROGRAMMER * pgm,
+ unsigned int requestid, unsigned int val, unsigned int index )
+{
+ int nbytes;
+ nbytes = usb_control_msg( PDATA(pgm)->usb_handle,
+ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ requestid,
+ val, index, // 2 bytes each of data
+ NULL, 0, // no data buffer in control messge
+ USB_TIMEOUT ); // default timeout
+ if(nbytes < 0){
+ avrdude_message(MSG_INFO, "\n%s: error: usbtiny_transmit: %s\n", progname, usb_strerror());
+ return -1;
+ }
+
+ return nbytes;
+}
+
+// Wrapper for simple usb_control_msg messages to receive data from programmer
+static int usb_in (PROGRAMMER * pgm,
+ unsigned int requestid, unsigned int val, unsigned int index,
+ unsigned char* buffer, int buflen, int bitclk )
+{
+ int nbytes;
+ int timeout;
+ int i;
+
+ // calculate the amout of time we expect the process to take by
+ // figuring the bit-clock time and buffer size and adding to the standard USB timeout.
+ timeout = USB_TIMEOUT + (buflen * bitclk) / 1000;
+
+ for (i = 0; i < 10; i++) {
+ nbytes = usb_control_msg( PDATA(pgm)->usb_handle,
+ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ requestid,
+ val, index,
+ (char *)buffer, buflen,
+ timeout);
+ if (nbytes == buflen) {
+ return nbytes;
+ }
+ PDATA(pgm)->retries++;
+ }
+ avrdude_message(MSG_INFO, "\n%s: error: usbtiny_receive: %s (expected %d, got %d)\n",
+ progname, usb_strerror(), buflen, nbytes);
+ return -1;
+}
+
+// Report the number of retries, and reset the counter.
+static void check_retries (PROGRAMMER * pgm, const char* operation)
+{
+ if (PDATA(pgm)->retries > 0 && quell_progress < 2) {
+ avrdude_message(MSG_INFO, "%s: %d retries during %s\n", progname,
+ PDATA(pgm)->retries, operation);
+ }
+ PDATA(pgm)->retries = 0;
+}
+
+// Wrapper for simple usb_control_msg messages to send data to programmer
+static int usb_out (PROGRAMMER * pgm,
+ unsigned int requestid, unsigned int val, unsigned int index,
+ unsigned char* buffer, int buflen, int bitclk )
+{
+ int nbytes;
+ int timeout;
+
+ // calculate the amout of time we expect the process to take by
+ // figuring the bit-clock time and buffer size and adding to the standard USB timeout.
+ timeout = USB_TIMEOUT + (buflen * bitclk) / 1000;
+
+ nbytes = usb_control_msg( PDATA(pgm)->usb_handle,
+ USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ requestid,
+ val, index,
+ (char *)buffer, buflen,
+ timeout);
+ if (nbytes != buflen) {
+ avrdude_message(MSG_INFO, "\n%s: error: usbtiny_send: %s (expected %d, got %d)\n",
+ progname, usb_strerror(), buflen, nbytes);
+ return -1;
+ }
+
+ return nbytes;
+}
+
+// Sometimes we just need to know the SPI command for the part to perform
+// a function. Here we wrap this request for an operation so that we
+// can just specify the part and operation and it'll do the right stuff
+// to get the information from AvrDude and send to the USBtiny
+static int usbtiny_avr_op (PROGRAMMER * pgm, AVRPART * p,
+ int op,
+ unsigned char *res)
+{
+ unsigned char cmd[4];
+
+ if (p->op[op] == NULL) {
+ avrdude_message(MSG_INFO, "Operation %d not defined for this chip!\n", op );
+ return -1;
+ }
+ memset(cmd, 0, sizeof(cmd));
+ avr_set_bits(p->op[op], cmd);
+
+ return pgm->cmd(pgm, cmd, res);
+}
+
+// ----------------------------------------------------------------------
+
+/* Find a device with the correct VID/PID match for USBtiny */
+
+static int usbtiny_open(PROGRAMMER* pgm, char* name)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev = 0;
+ char *bus_name = NULL;
+ char *dev_name = NULL;
+ int vid, pid;
+
+ // if no -P was given or '-P usb' was given
+ if(strcmp(name, "usb") == 0)
+ name = NULL;
+ else {
+ // calculate bus and device names from -P option
+ const size_t usb_len = strlen("usb");
+ if(strncmp(name, "usb", usb_len) == 0 && ':' == name[usb_len]) {
+ bus_name = name + usb_len + 1;
+ dev_name = strchr(bus_name, ':');
+ if(NULL != dev_name)
+ *dev_name++ = '\0';
+ }
+ }
+
+ usb_init(); // initialize the libusb system
+ usb_find_busses(); // have libusb scan all the usb busses available
+ usb_find_devices(); // have libusb scan all the usb devices available
+
+ PDATA(pgm)->usb_handle = NULL;
+
+ if (pgm->usbvid)
+ vid = pgm->usbvid;
+ else
+ vid = USBTINY_VENDOR_DEFAULT;
+
+ LNODEID usbpid = lfirst(pgm->usbpid);
+ if (usbpid) {
+ pid = *(int *)(ldata(usbpid));
+ if (lnext(usbpid))
+ avrdude_message(MSG_INFO, "%s: Warning: using PID 0x%04x, ignoring remaining PIDs in list\n",
+ progname, pid);
+ } else {
+ pid = USBTINY_PRODUCT_DEFAULT;
+ }
+
+
+ // now we iterate through all the busses and devices
+ for ( bus = usb_busses; bus; bus = bus->next ) {
+ for ( dev = bus->devices; dev; dev = dev->next ) {
+ if (dev->descriptor.idVendor == vid
+ && dev->descriptor.idProduct == pid ) { // found match?
+ avrdude_message(MSG_NOTICE, "%s: usbdev_open(): Found USBtinyISP, bus:device: %s:%s\n",
+ progname, bus->dirname, dev->filename);
+ // if -P was given, match device by device name and bus name
+ if(name != NULL &&
+ (NULL == dev_name ||
+ strcmp(bus->dirname, bus_name) ||
+ strcmp(dev->filename, dev_name)))
+ continue;
+ PDATA(pgm)->usb_handle = usb_open(dev); // attempt to connect to device
+
+ // wrong permissions or something?
+ if (!PDATA(pgm)->usb_handle) {
+ avrdude_message(MSG_INFO, "%s: Warning: cannot open USB device: %s\n",
+ progname, usb_strerror());
+ continue;
+ }
+ }
+ }
+ }
+
+ if(NULL != name && NULL == dev_name) {
+ avrdude_message(MSG_INFO, "%s: Error: Invalid -P value: '%s'\n", progname, name);
+ avrdude_message(MSG_INFO, "%sUse -P usb:bus:device\n", progbuf);
+ return -1;
+ }
+ if (!PDATA(pgm)->usb_handle) {
+ avrdude_message(MSG_INFO, "%s: Error: Could not find USBtiny device (0x%x/0x%x)\n",
+ progname, vid, pid );
+ return -1;
+ }
+
+ return 0; // If we got here, we must have found a good USB device
+}
+
+/* Clean up the handle for the usbtiny */
+static void usbtiny_close ( PROGRAMMER* pgm )
+{
+ if (! PDATA(pgm)->usb_handle) {
+ return; // not a valid handle, bail!
+ }
+ usb_close(PDATA(pgm)->usb_handle); // ask libusb to clean up
+ PDATA(pgm)->usb_handle = NULL;
+}
+
+/* A simple calculator function determines the maximum size of data we can
+ shove through a USB connection without getting errors */
+static void usbtiny_set_chunk_size (PROGRAMMER * pgm, int period)
+{
+ PDATA(pgm)->chunk_size = CHUNK_SIZE; // start with the maximum (default)
+ while (PDATA(pgm)->chunk_size > 8 && period > 16) {
+ // Reduce the chunk size for a slow SCK to reduce
+ // the maximum time of a single USB transfer.
+ PDATA(pgm)->chunk_size >>= 1;
+ period >>= 1;
+ }
+}
+
+/* Given a SCK bit-clock speed (in useconds) we verify its an OK speed and tell the
+ USBtiny to update itself to the new frequency */
+static int usbtiny_set_sck_period (PROGRAMMER *pgm, double v)
+{
+ PDATA(pgm)->sck_period = (int)(v * 1e6 + 0.5); // convert from us to 'int', the 0.5 is for rounding up
+
+ // Make sure its not 0, as that will confuse the usbtiny
+ if (PDATA(pgm)->sck_period < SCK_MIN)
+ PDATA(pgm)->sck_period = SCK_MIN;
+
+ // We can't go slower, due to the byte-size of the clock variable
+ if (PDATA(pgm)->sck_period > SCK_MAX)
+ PDATA(pgm)->sck_period = SCK_MAX;
+
+ avrdude_message(MSG_NOTICE, "%s: Setting SCK period to %d usec\n", progname,
+ PDATA(pgm)->sck_period );
+
+ // send the command to the usbtiny device.
+ // MEME: for at90's fix resetstate?
+ if (usb_control(pgm, USBTINY_POWERUP, PDATA(pgm)->sck_period, RESET_LOW) < 0)
+ return -1;
+
+ // with the new speed, we'll have to update how much data we send per usb transfer
+ usbtiny_set_chunk_size(pgm, PDATA(pgm)->sck_period);
+ return 0;
+}
+
+
+static int usbtiny_initialize (PROGRAMMER *pgm, AVRPART *p )
+{
+ unsigned char res[4]; // store the response from usbtinyisp
+
+ // Check for bit-clock and tell the usbtiny to adjust itself
+ if (pgm->bitclock > 0.0) {
+ // -B option specified: convert to valid range for sck_period
+ usbtiny_set_sck_period(pgm, pgm->bitclock);
+ } else {
+ // -B option not specified: use default
+ PDATA(pgm)->sck_period = SCK_DEFAULT;
+ avrdude_message(MSG_NOTICE, "%s: Using SCK period of %d usec\n",
+ progname, PDATA(pgm)->sck_period );
+ if (usb_control(pgm, USBTINY_POWERUP,
+ PDATA(pgm)->sck_period, RESET_LOW ) < 0)
+ return -1;
+ usbtiny_set_chunk_size(pgm, PDATA(pgm)->sck_period);
+ }
+
+ // Let the device wake up.
+ usleep(50000);
+
+ // Attempt to use the underlying avrdude methods to connect (MEME: is this kosher?)
+ if (! usbtiny_avr_op(pgm, p, AVR_OP_PGM_ENABLE, res)) {
+ // no response, RESET and try again
+ if (usb_control(pgm, USBTINY_POWERUP,
+ PDATA(pgm)->sck_period, RESET_HIGH) < 0 ||
+ usb_control(pgm, USBTINY_POWERUP,
+ PDATA(pgm)->sck_period, RESET_LOW) < 0)
+ return -1;
+ usleep(50000);
+ if ( ! usbtiny_avr_op( pgm, p, AVR_OP_PGM_ENABLE, res)) {
+ // give up
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Tell the USBtiny to release the output pins, etc */
+static void usbtiny_powerdown(PROGRAMMER * pgm)
+{
+ if (!PDATA(pgm)->usb_handle) {
+ return; // wasn't connected in the first place
+ }
+ usb_control(pgm, USBTINY_POWERDOWN, 0, 0); // Send USB control command to device
+}
+
+/* Send a 4-byte SPI command to the USBtinyISP for execution
+ This procedure is used by higher-level Avrdude procedures */
+static int usbtiny_cmd(PROGRAMMER * pgm, const unsigned char *cmd, unsigned char *res)
+{
+ int nbytes;
+
+ // Make sure its empty so we don't read previous calls if it fails
+ memset(res, '\0', 4 );
+
+ nbytes = usb_in( pgm, USBTINY_SPI,
+ (cmd[1] << 8) | cmd[0], // convert to 16-bit words
+ (cmd[3] << 8) | cmd[2], // "
+ res, 4, 8 * PDATA(pgm)->sck_period );
+ if (nbytes < 0)
+ return -1;
+ check_retries(pgm, "SPI command");
+ // print out the data we sent and received
+ avrdude_message(MSG_NOTICE2, "CMD: [%02x %02x %02x %02x] [%02x %02x %02x %02x]\n",
+ cmd[0], cmd[1], cmd[2], cmd[3],
+ res[0], res[1], res[2], res[3] );
+ return ((nbytes == 4) && // should have read 4 bytes
+ res[2] == cmd[1]); // AVR's do a delayed-echo thing
+}
+
+/* Send the chip-erase command */
+static int usbtiny_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+ unsigned char res[4];
+
+ if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+ avrdude_message(MSG_INFO, "Chip erase instruction not defined for part \"%s\"\n",
+ p->desc);
+ return -1;
+ }
+
+ // get the command for erasing this chip and transmit to avrdude
+ if (! usbtiny_avr_op( pgm, p, AVR_OP_CHIP_ERASE, res )) {
+ return -1;
+ }
+ usleep( p->chip_erase_delay );
+
+ // prepare for further instruction
+ pgm->initialize(pgm, p);
+
+ return 0;
+}
+
+// These are required functions but don't actually do anything
+static void usbtiny_enable ( PROGRAMMER* pgm ) {}
+
+static void usbtiny_disable ( PROGRAMMER* pgm ) {}
+
+
+/* To speed up programming and reading, we do a 'chunked' read.
+ * We request just the data itself and the USBtiny uses the SPI function
+ * given to read in the data. Much faster than sending a 4-byte SPI request
+ * per byte
+*/
+static int usbtiny_paged_load (PROGRAMMER * pgm, AVRPART * p, AVRMEM* m,
+ unsigned int page_size,
+ unsigned int addr, unsigned int n_bytes)
+{
+ unsigned int maxaddr = addr + n_bytes;
+ int chunk;
+ int function;
+
+
+ // First determine what we're doing
+ if (strcmp( m->desc, "flash" ) == 0) {
+ function = USBTINY_FLASH_READ;
+ } else {
+ function = USBTINY_EEPROM_READ;
+ }
+
+ for (; addr < maxaddr; addr += chunk) {
+ chunk = PDATA(pgm)->chunk_size; // start with the maximum chunk size possible
+
+ // Send the chunk of data to the USBtiny with the function we want
+ // to perform
+ if (usb_in(pgm,
+ function, // EEPROM or flash
+ 0, // delay between SPI commands
+ addr, // address in memory
+ m->buf + addr, // pointer to where we store data
+ chunk, // number of bytes
+ 32 * PDATA(pgm)->sck_period) // each byte gets turned into a 4-byte SPI cmd
+ < 0) {
+ // usb_in() multiplies this per byte.
+ return -1;
+ }
+ }
+
+ check_retries(pgm, "read");
+ return n_bytes;
+}
+
+/* To speed up programming and reading, we do a 'chunked' write.
+ * We send just the data itself and the USBtiny uses the SPI function
+ * given to write the data. Much faster than sending a 4-byte SPI request
+ * per byte.
+*/
+static int usbtiny_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
+ unsigned int page_size,
+ unsigned int addr, unsigned int n_bytes)
+{
+ unsigned int maxaddr = addr + n_bytes;
+ int chunk; // Size of data to write at once
+ int next;
+ int function; // which SPI command to use
+ int delay; // delay required between SPI commands
+
+ // First determine what we're doing
+ if (strcmp( m->desc, "flash" ) == 0) {
+ function = USBTINY_FLASH_WRITE;
+ } else {
+ function = USBTINY_EEPROM_WRITE;
+ }
+
+ delay = 0;
+ if (! m->paged) {
+ unsigned int poll_value;
+ // Does this chip not support paged writes?
+ poll_value = (m->readback[1] << 8) | m->readback[0];
+ if (usb_control(pgm, USBTINY_POLL_BYTES, poll_value, 0 ) < 0)
+ return -1;
+ delay = m->max_write_delay;
+ }
+
+ for (; addr < maxaddr; addr += chunk) {
+ // start with the max chunk size
+ chunk = PDATA(pgm)->chunk_size;
+
+ // we can only write a page at a time anyways
+ if (m->paged && chunk > page_size)
+ chunk = page_size;
+
+ if (usb_out(pgm,
+ function, // Flash or EEPROM
+ delay, // How much to wait between each byte
+ addr, // Address in memory
+ m->buf + addr, // Pointer to data
+ chunk, // Number of bytes to write
+ 32 * PDATA(pgm)->sck_period + delay // each byte gets turned into a
+ // 4-byte SPI cmd usb_out() multiplies
+ // this per byte. Then add the cmd-delay
+ ) < 0) {
+ return -1;
+ }
+
+ next = addr + chunk; // Calculate what address we're at now
+ if (m->paged
+ && ((next % page_size) == 0 || next == maxaddr) ) {
+ // If we're at a page boundary, send the SPI command to flush it.
+ avr_write_page(pgm, p, m, (unsigned long) addr);
+ }
+ }
+ return n_bytes;
+}
+
+void usbtiny_initpgm ( PROGRAMMER* pgm )
+{
+ strcpy(pgm->type, "USBtiny");
+
+ /* Mandatory Functions */
+ pgm->initialize = usbtiny_initialize;
+ pgm->enable = usbtiny_enable;
+ pgm->disable = usbtiny_disable;
+ pgm->program_enable = NULL;
+ pgm->chip_erase = usbtiny_chip_erase;
+ pgm->cmd = usbtiny_cmd;
+ pgm->open = usbtiny_open;
+ pgm->close = usbtiny_close;
+ pgm->read_byte = avr_read_byte_default;
+ pgm->write_byte = avr_write_byte_default;
+
+ /* Optional Functions */
+ pgm->powerup = NULL;
+ pgm->powerdown = usbtiny_powerdown;
+ pgm->paged_load = usbtiny_paged_load;
+ pgm->paged_write = usbtiny_paged_write;
+ pgm->set_sck_period = usbtiny_set_sck_period;
+ pgm->setup = usbtiny_setup;
+ pgm->teardown = usbtiny_teardown;
+}
+
+#else /* !HAVE_LIBUSB */
+
+// Give a proper error if we were not compiled with libusb
+
+static int usbtiny_nousb_open(struct programmer_t *pgm, char * name)
+{
+ avrdude_message(MSG_INFO, "%s: error: no usb support. Please compile again with libusb installed.\n",
+ progname);
+
+ return -1;
+}
+
+void usbtiny_initpgm(PROGRAMMER * pgm)
+{
+ strcpy(pgm->type, "usbtiny");
+
+ pgm->open = usbtiny_nousb_open;
+}
+
+#endif /* HAVE_LIBUSB */
+
+const char usbtiny_desc[] = "Driver for \"usbtiny\"-type programmers";
+