Patchwork =?utf-8?q?=5BPATCH=5D=5BRESEND=5D_uisp=5Fspi_support?=

login
register
about
Submitter Andrew
Date 2014-06-08 19:18:18
Message ID <e4b233c21e184ed6098885e22fad1372@mail.ncrmnt.org>
Download mbox | patch
Permalink /patch/4182/
State New
Headers show

Comments

Andrew - 2014-06-08 19:18:18
Hello all!

This is the resubmit of the uISP programmer support patch. A list of 
fixes follows:

* Proper protocol versioning support.
* Programmer now reports its name, SPI speed and CPU frequency
* Programmer now allows setting the SPI frequency. While now this
   is done using spi_freq=xxx parameter (in Khz)
* Programmer can now have an arbitary number of SPI flashes attached (on 
different SPI buses)
   and even allows several instances  of flashrom to work with each of 
these flashes at
   the same time (option idx=N specifies the bus number to use, default 
is 0).
* Speed improvements due to minor optimizations in firmware.
   (2Mib flash read was 2m13s now is only 1m15s)
* Firmware can now be easily extended to add hardware other than uISP, 
even based
   on other MCUs.
* Code cleaned up
* Firmware code reworked for better portability. Since it's based off 
antares, STM32 is
   the next target.

A full writeup with hardware pictures and nice benchmark graphs can be 
found in my
blog here: 
http://ncrmnt.org/wp/2014/06/08/flashrom-benchmark-buspirate-vs-uisp/

Firmware source code can be found on github here:
https://github.com/uISP/uisp-app-flashprog
Антон Кочков - 2014-07-06 10:29:11
Hello!
Writing this email because google marked this thread as spam, so I suppose
that could be not only my case.
So I'm bumping this thread.
Any updates on this?


2014-06-08 23:18 GMT+04:00 Andrew <andrew@ncrmnt.org>:

> Hello all!
>
> This is the resubmit of the uISP programmer support patch. A list of fixes
> follows:
>
> * Proper protocol versioning support.
> * Programmer now reports its name, SPI speed and CPU frequency
> * Programmer now allows setting the SPI frequency. While now this
>   is done using spi_freq=xxx parameter (in Khz)
> * Programmer can now have an arbitary number of SPI flashes attached (on
> different SPI buses)
>   and even allows several instances  of flashrom to work with each of
> these flashes at
>   the same time (option idx=N specifies the bus number to use, default is
> 0).
> * Speed improvements due to minor optimizations in firmware.
>   (2Mib flash read was 2m13s now is only 1m15s)
> * Firmware can now be easily extended to add hardware other than uISP,
> even based
>   on other MCUs.
> * Code cleaned up
> * Firmware code reworked for better portability. Since it's based off
> antares, STM32 is
>   the next target.
>
> A full writeup with hardware pictures and nice benchmark graphs can be
> found in my
> blog here: http://ncrmnt.org/wp/2014/06/08/flashrom-benchmark-
> buspirate-vs-uisp/
>
> Firmware source code can be found on github here:
> https://github.com/uISP/uisp-app-flashprog
>
> --
> Regards,
> Andrew
>
> _______________________________________________
> flashrom mailing list
> flashrom@flashrom.org
> http://www.flashrom.org/mailman/listinfo/flashrom
>

Patch

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