Index: uisp_spi.c
===================================================================
--- uisp_spi.c	(revision 0)
+++ uisp_spi.c	(revision 0)
@@ -0,0 +1,417 @@
+/*
+ * This file is part of the flashrom project.
+ * uISP-flashprog programmer interface
+ *
+ * Copyright (C) 2014 Andew "Necromant" Andrianov <andrew@ncrmnt.org>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <usb.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+#include "chipdrivers.h"
+
+
+struct flashprog_verinfo {
+	char pgmname[16];   /* Programmer name            */
+	uint16_t max_rq;    /* Maximum RQ number          */
+	uint16_t api_ver;   /* API version                */
+	uint16_t spi_freq;  /* Current SPI frequency      */
+	uint16_t cpu_freq;  /* CPU frequency              */
+	uint16_t num_spi;   /* Number of SPI devices here */
+} __attribute__ ((packed));
+
+
+#define FLASHPROG_API_VER  1
+
+
+enum { 
+	RQ_VERINFO,
+	RQ_SPI_SET_SPEED,
+	RQ_SPI_GET_SPEED,
+	RQ_SPI_CS,
+	RQ_SPI_IO,
+	/* Insert new requests here */ 
+	RQ_INVALID
+};
+
+#define UISP_TIMEOUT 6000
+
+static usb_dev_handle *hndl;
+static struct flashprog_verinfo verinfo;
+static uint16_t dev_index;
+
+#define UISP_VID   0x1d50
+#define UISP_PID   0x6032
+#define UISP_MSTR  "www.ncrmnt.org"
+#define UISP_PSTR  "uISP-flashprog"
+
+const struct dev_entry devs_usbuispspi[] = {
+	{UISP_VID, UISP_PID, OK, UISP_MSTR, UISP_PSTR},
+	{}
+};
+
+static int  usb_get_string_ascii(usb_dev_handle *dev, int index, int langid, char *buf, int buflen)
+{
+	char    buffer[256];
+	int     rval, i;
+	
+	if((rval = usb_control_msg(dev, 
+				   USB_ENDPOINT_IN, 
+				   USB_REQ_GET_DESCRIPTOR, 
+				   (USB_DT_STRING << 8) + index, 
+				   langid, buffer, sizeof(buffer), 
+				   1000)) < 0)
+		return rval;
+	if(buffer[1] != USB_DT_STRING)
+		return 0;
+	if((unsigned char)buffer[0] < rval)
+		rval = (unsigned char)buffer[0];
+	rval /= 2;
+	/* lossy conversion to ISO Latin1 */
+	for(i=1; i<rval; i++) {
+		if(i > buflen)  /* destination buffer overflow */
+			break;
+		buf[i-1] = buffer[2 * i];
+		if(buffer[2 * i + 1] != 0)  /* outside of ISO Latin1 range */
+			buf[i-1] = '?';
+	}
+	buf[i-1] = 0;
+	return i-1;
+}
+
+
+static int usb_match_string(usb_dev_handle *handle, int index, char* string)
+{
+	char tmp[256];
+	if (string == NULL)
+		return 1; /* NULL matches anything */
+	usb_get_string_ascii(handle, index, 0x409, tmp, 256);
+	return (strcmp(string,tmp)==0);
+}
+
+static usb_dev_handle *usb_check_device(struct usb_device *dev,
+				 char *vendor_name, 
+				 char *product_name, 
+				 char *serial)
+{
+	usb_dev_handle      *handle = usb_open(dev);
+	if(!handle) {
+		msg_perr("uisp_spi: Error: Unable to query device info (udev rules problem?)\n");
+		return NULL;
+	}
+	if (
+		usb_match_string(handle, dev->descriptor.iManufacturer, vendor_name) &&
+		usb_match_string(handle, dev->descriptor.iProduct,      product_name) &&
+		usb_match_string(handle, dev->descriptor.iSerialNumber, serial)
+		) {
+		return handle;
+	}
+	usb_close(handle);
+	return NULL;
+	
+}
+
+static usb_dev_handle *nc_usb_open(int vendor, int product, char *vendor_name, char *product_name, char *serial)
+{
+	struct usb_bus      *bus;
+	struct usb_device   *dev;
+	usb_dev_handle      *handle = NULL;
+	
+
+	usb_find_busses();
+	usb_find_devices();
+
+	for(bus=usb_get_busses(); bus; bus=bus->next) {
+		for(dev=bus->devices; dev; dev=dev->next) {
+			            if(dev->descriptor.idVendor == vendor && 
+				       dev->descriptor.idProduct == product) {
+					    handle = usb_check_device(dev, vendor_name, product_name, serial);
+					    if (handle)
+						    return handle;
+				    }
+		}
+	}
+	return NULL;
+}
+
+
+
+static int send_read(unsigned int readcnt, void *readarr)
+{
+	msg_pspew("%s\n", __func__);
+	int ret; 
+	ret = usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, // bRequestType
+		RQ_SPI_IO,      // bRequest
+		0,              // wValue
+		dev_index,              // wIndex
+		readarr,             // pointer to source buffer
+		readcnt,  // wLength
+		UISP_TIMEOUT
+		);
+	msg_pspew("%s: control ret %d \n", __func__, ret); 
+	if (ret != readcnt) {
+		perror(__func__);
+		return -1; 
+	}
+	return 0;
+}
+
+static int do_control(int rq, int value, int index)
+{
+	int ret; 
+	ret =  usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, // bRequestType
+		rq,      // bRequest
+		value,              // wValue
+		index,              // wIndex
+		NULL,             // pointer to destination buffer
+		0,  // wLength
+		UISP_TIMEOUT
+		);
+	msg_pspew("%s: control ret %d \n", __func__, ret); 
+	return ret;
+}
+
+static int uisp_request_pgm_info() 
+{
+	int ret; 
+	ret = usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, // bRequestType
+		RQ_VERINFO,      // bRequest
+		0,              // wValue
+		dev_index,              // wIndex
+		(char *) &verinfo,             // pointer to source buffer
+		sizeof(verinfo),  // wLength
+		UISP_TIMEOUT
+		);
+
+	msg_pspew("%s: control ret %d \n", __func__, ret); 
+
+	if (ret != sizeof(verinfo)) {
+		perror(__func__);
+		return -1; 
+	}
+
+	return 0;
+}
+
+static int uisp_get_spi_freq() 
+{
+	int ret; 
+	uint16_t freq;
+	ret = usb_control_msg(
+		hndl,             
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 
+		RQ_SPI_GET_SPEED,
+		0,             
+		dev_index,     
+		(char *) &freq,
+		sizeof(freq),  
+		UISP_TIMEOUT
+		);
+	
+	msg_pspew("%s: control ret %d freq %u \n", __func__, ret, freq); 
+
+	if (ret != sizeof(freq)) {
+		perror(__func__);
+		return -1; 
+	}
+
+	return (int) freq;
+}
+
+
+static int send_write(unsigned int writecnt, void *writearr)
+{
+	msg_pspew("%s\n", __func__);
+	int ret; 
+	ret = usb_control_msg(
+		hndl,             // handle obtained with usb_open()
+		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, // bRequestType
+		RQ_SPI_IO,      // bRequest
+		0,              // wValue
+		0,              // wIndex
+		writearr,             // pointer to source buffer
+		writecnt,  // wLength
+		UISP_TIMEOUT
+		);
+	msg_pspew("%s: control ret %d ", __func__, ret); 
+	if (ret != writecnt) {
+		perror(__func__);
+		return -1; 
+	}
+	return 0;
+}
+
+
+static int uisp_spi_send_command(struct flashctx *flash,
+				     unsigned int writecnt,
+				     unsigned int readcnt,
+				     const unsigned char *writearr,
+				     unsigned char *readarr)
+{
+	msg_pspew("%s %d %d\n", __func__, readcnt, writecnt);
+	
+	int ret = 0;
+	
+	ret = do_control(RQ_SPI_CS, 0, 0);
+
+	if (!ret && writecnt)
+		ret = send_write(writecnt, (void *) writearr);
+
+	if (!ret && readcnt)
+		ret = send_read(readcnt, readarr);
+
+	ret = do_control(RQ_SPI_CS, 1, 0);
+
+	return ret;
+}
+
+
+static int uisp_spi_read(struct flashctx *flash, uint8_t *buf,
+			 unsigned int start, unsigned int len);
+
+/* We have 4096 maximum transfer length for control ep
+ * hardcoded in the usb stack. 
+ * Make sure to have long-transfers enabled
+ */
+static const struct spi_programmer spi_programmer_uisp = {
+	.type		= SPI_CONTROLLER_UISP,
+	.max_data_read	= 4096,
+	.max_data_write	= 4096,
+	.command	= uisp_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= uisp_spi_read,
+	.write_256	= default_spi_write_256,
+	.write_aai	= default_spi_write_aai,
+};
+
+static int uisp_spi_read(struct flashctx *flash, uint8_t *buf,
+			    unsigned int start, unsigned int len)
+{
+	unsigned int i, cur_len;
+	const unsigned int max_read = spi_programmer_uisp.max_data_read;
+	for (i = 0; i < len; i += cur_len) {
+		int ret;
+		cur_len = min(max_read, (len - i));
+		ret = spi_nbyte_read(flash, start + i, buf + i, cur_len);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+
+int uisp_spi_init(void)
+{	
+	int ret; 
+	msg_pspew("%s\n", __func__);
+	usb_init();
+
+	uint16_t spi_freq = 0;
+	
+	char *s_idx = extract_programmer_param("idx");
+	if (s_idx && strlen(s_idx)) {
+		dev_index = atoi(s_idx);
+	}
+	
+	char *s_freq = extract_programmer_param("spi_freq");
+	if (s_freq && strlen(s_freq)) {
+		spi_freq = (uint16_t) atoi(s_freq);
+	}
+	
+	/* 
+	 * TODO: Iterate over a list of compatible device ids. 
+	 * While there's only one - ignore it 
+	 */
+
+	hndl = nc_usb_open(UISP_VID, UISP_PID, UISP_MSTR, UISP_PSTR, NULL);
+	if (!hndl)  
+		return 1;
+
+	uisp_request_pgm_info();
+
+	msg_pinfo("uisp_spi: Programmer name is \"%s\" \n", verinfo.pgmname);
+	msg_pinfo("uisp_spi: Programmer CPU speed is %u kHz\n", verinfo.cpu_freq);
+	msg_pinfo("uisp_spi: Programmer maximum SPI speed is %u kHz\n", verinfo.spi_freq);
+	msg_pinfo("uisp_spi: Programmer supports %u SPI bus(es)\n", verinfo.num_spi);
+
+	
+	if (verinfo.api_ver != FLASHPROG_API_VER) {
+		msg_perr("uisp_spi: Error: flashrom expects API %d device has API %d\n", 
+			 FLASHPROG_API_VER, verinfo.api_ver);
+		return -1;
+	}
+	
+	if (RQ_INVALID < verinfo.max_rq) {
+		msg_pwarn("uisp_spi: flashrom supports fewer requests than device exports\n");
+		msg_pwarn("uisp_spi: Your version of flashrom may be outdated!\n");
+	}
+	
+	if (RQ_INVALID > verinfo.max_rq) {
+		msg_pwarn("uisp_spi: flashrom supports more requests than device exports\n");
+		msg_pwarn("uisp_spi: Your device firmware may be outdated\n");
+		msg_pwarn("uisp_spi: Continue at your own risk!\n");
+	}
+
+	if (dev_index >= verinfo.num_spi) {
+		msg_perr("uisp_spi: Error: You want to use SPI bus %d but programmer only has %d bus(es)\n", 
+			 dev_index, verinfo.num_spi);
+		return -1;
+	}
+
+	if (spi_freq && (spi_freq > verinfo.spi_freq)) {
+		msg_perr("uisp_spi: Error: Requested SPI speed %u kHz but programmer can only go as fast"
+			 " as %d kHz\n", 
+			 spi_freq, verinfo.spi_freq);
+		return -1;
+	}	
+	
+	/* Set the actual spi frequency */ 
+	if (spi_freq) { 
+		msg_pinfo("uisp_spi: Requesting SPI speed of %u kHz\n", spi_freq);
+		ret = do_control(RQ_SPI_SET_SPEED, spi_freq, dev_index);
+		if (ret < 0) { 
+			perror(__func__);
+			return -1;
+		}
+	}
+
+	int actual_freq = uisp_get_spi_freq(); 
+	if (actual_freq < 0) {
+		perror(__func__);
+		return -1;
+	}
+	
+	msg_pinfo("uisp_spi: Using SPI bus %d, actual SPI freq is %d kHz\n", 
+		  dev_index, actual_freq);
+	
+	register_spi_programmer(&spi_programmer_uisp);
+	
+	return 0;
+}
+
Index: Makefile
===================================================================
--- Makefile	(revision 1809)
+++ Makefile	(working copy)
@@ -122,6 +122,11 @@
 else
 override CONFIG_BUSPIRATE_SPI = no
 endif
+ifeq ($(CONFIG_UISP_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_UISP_SPI=yes
+else
+override CONFIG_UISP_SPI = no
+endif
 ifeq ($(CONFIG_SERPROG), yes)
 UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
 else
@@ -251,6 +256,11 @@
 else
 override CONFIG_BUSPIRATE_SPI = no
 endif
+ifeq ($(CONFIG_UISP_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_UISP_SPI=yes
+else
+override CONFIG_UISP_SPI = no
+endif
 ifeq ($(CONFIG_SERPROG), yes)
 UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
 else
@@ -427,6 +437,9 @@
 # Always enable Bus Pirate SPI for now.
 CONFIG_BUSPIRATE_SPI ?= yes
 
+#Always enable uISP for now
+CONFIG_UISP_SPI ?= yes
+
 # Disable Dediprog SF100 until support is complete and tested.
 CONFIG_DEDIPROG ?= no
 
@@ -623,6 +636,13 @@
 NEED_SERIAL := yes
 endif
 
+ifeq ($(CONFIG_UISP_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_UISP_SPI=1'
+PROGRAMMER_OBJS += uisp_spi.o
+NEED_USB := yes
+endif
+
+
 ifeq ($(CONFIG_DEDIPROG), yes)
 FEATURE_CFLAGS += -D'CONFIG_DEDIPROG=1'
 PROGRAMMER_OBJS += dediprog.o
Index: flashrom.c
===================================================================
--- flashrom.c	(revision 1809)
+++ flashrom.c	(working copy)
@@ -333,6 +333,20 @@
 	},
 #endif
 
+#if CONFIG_UISP_SPI == 1
+	{
+		.name			= "uisp_spi",
+		.type			= USB,
+		.devs.note		= "Necromant's uISP dongle (flashprog app)\n",
+		.devs.dev               = devs_usbuispspi,
+		.init			= uisp_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
+	},
+#endif
+
+
 	{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
 };
 
Index: programmer.h
===================================================================
--- programmer.h	(revision 1809)
+++ programmer.h	(working copy)
@@ -93,6 +93,9 @@
 #if CONFIG_USBBLASTER_SPI == 1
 	PROGRAMMER_USBBLASTER_SPI,
 #endif
+#if CONFIG_UISP_SPI == 1
+	PROGRAMMER_UISP_SPI,
+#endif
 	PROGRAMMER_INVALID /* This must always be the last entry. */
 };
 
@@ -474,6 +477,12 @@
 int buspirate_spi_init(void);
 #endif
 
+/* uisp_spi.c */
+#if CONFIG_UISP_SPI == 1
+int uisp_spi_init(void);
+extern const struct dev_entry devs_usbuispspi[];
+#endif
+
 /* linux_spi.c */
 #if CONFIG_LINUX_SPI == 1
 int linux_spi_init(void);
@@ -538,6 +547,9 @@
 #if CONFIG_USBBLASTER_SPI == 1
 	SPI_CONTROLLER_USBBLASTER,
 #endif
+#if CONFIG_UISP_SPI == 1
+	SPI_CONTROLLER_UISP,
+#endif
 };
 
 #define MAX_DATA_UNSPECIFIED 0
