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 + * + * 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 +#include +#include +#include +#include +#include +#include +#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 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