Hello Miklós Márton,
I'd like you to do a code review. Please visit
https://review.coreboot.org/25683
to review the following change.
Change subject: - Added support for National Instruments USB-845x devices ......................................................................
- Added support for National Instruments USB-845x devices
Change-Id: I9477b6f0193bfdf20bbe63421a7fb97b597ec549 Signed-off-by: Miklós Márton martonmiklosqdev@gmail.com --- M Makefile M flashrom.8.tmpl M flashrom.c A ni845x_spi.c M programmer.h 5 files changed, 636 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/83/25683/1
diff --git a/Makefile b/Makefile index e26b41e..d01a554 100644 --- a/Makefile +++ b/Makefile @@ -193,6 +193,10 @@ # Some functions provided by Microsoft do not work as described in C99 specifications. This macro fixes that # for MinGW. See http://sourceforge.net/p/mingw-w64/wiki2/printf%20and%20scanf%20family/ */ FLASHROM_CFLAGS += -D__USE_MINGW_ANSI_STDIO=1 + +# National Instruments USB-845x is Windows only for now +CONFIG_NI845X_SPI ?= no + # For now we disable all PCI-based programmers on Windows/MinGW (no libpci). ifeq ($(CONFIG_INTERNAL), yes) UNSUPPORTED_FEATURES += CONFIG_INTERNAL=yes @@ -281,6 +285,15 @@ endif endif
+ifneq ($(TARGET_OS), MinGW) +# NI USB-845x only supported on Windows at the moment +ifeq ($(CONFIG_NI845X_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_NI845X_SPI=yes +else +override CONFIG_NI845X_SPI = no +endif +endif + ifeq ($(TARGET_OS), libpayload) ifeq ($(MAKECMDGOALS),) .DEFAULT_GOAL := libflashrom.a @@ -925,6 +938,17 @@ NEED_LIBUSB1 += CONFIG_CH341A_SPI endif
+ifeq ($(CONFIG_NI845X_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_NI845X_SPI=1' +NI845X_LIBS += -L'C:\Program Files (x86)\National Instruments\NI-845x\MS Visual C' +NI845X_LIBS += -L'C:\Program Files\National Instruments\NI-845x\MS Visual C' +NI845X_INCLUDES += -I'C:\Program Files (x86)\National Instruments\NI-845x\MS Visual C' +NI845X_INCLUDES += -I'C:\Program Files\National Instruments\NI-845x\MS Visual C' +FEATURE_CFLAGS += $(NI845X_INCLUDES) +LIBS += -lni845x +PROGRAMMER_OBJS += ni845x_spi.o +endif + ifneq ($(NEED_SERIAL), ) LIB_OBJS += serial.o custom_baud.o endif @@ -1020,7 +1044,7 @@ endif
$(PROGRAM)$(EXEC_SUFFIX): $(OBJS) - $(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(OBJS) $(LIBS) $(PCILIBS) $(FEATURE_LIBS) $(USBLIBS) $(USB1LIBS) + $(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(OBJS) $(LIBS) $(PCILIBS) $(FEATURE_LIBS) $(USBLIBS) $(USB1LIBS) $(NI845X_LIBS)
libflashrom.a: $(LIBFLASHROM_OBJS) $(AR) rcs $@ $^ @@ -1319,6 +1343,23 @@ endef export CLOCK_GETTIME_TEST
+define NI845X_TEST +#include <ni845x.h> + +int main(int argc, char ** argv) +{ + (void) argc; + (void) argv; + char I2C_Device[256]; + NiHandle Dev_Handle; + uInt32 NumberFound = 0; + ni845xFindDevice(I2C_Device, &Dev_Handle, &NumberFound); + ni845xCloseFindDeviceHandle(Dev_Handle); + return 0; +} +endef +export NI845X_TEST + features: compiler @echo "FEATURES := yes" > .features.tmp ifneq ($(NEED_LIBFTDI), ) @@ -1355,6 +1396,15 @@ ( echo "no."; echo "LINUX_I2C_SUPPORT := no" >> .features.tmp ) } \ 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE) endif +ifeq ($(CONFIG_NI845X_SPI), yes) + @printf "Checking for NI USB-845x installation... " | tee -a $(BUILD_DETAILS_FILE) + @echo "$$NI845X_TEST" > .featuretest.c + @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(NI845X_INCLUDES) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(NI845X_LIBS) $(LIBS)" >>$(BUILD_DETAILS_FILE) + @ { $(CC) $(CPPFLAGS) $(CFLAGS) $(NI845X_INCLUDES) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(NI845X_LIBS) $(LIBS) >&2 && \ + ( echo "yes."; echo "NI845X_SUPPORT := yes" >> .features.tmp ) || \ + ( echo "no."; echo "NI845X_SUPPORT := no" >> .features.tmp ) } \ + 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE) +endif @printf "Checking for utsname support... " | tee -a $(BUILD_DETAILS_FILE) @echo "$$UTSNAME_TEST" > .featuretest.c @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX)" >>$(BUILD_DETAILS_FILE) diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 1600113..1cf8da8 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -301,6 +301,8 @@ .sp .BR "* ch341a_spi" " (for SPI flash ROMs attached to WCH CH341A)" .sp +.BR "* ni845x_spi" " (for SPI flash ROMs attached to National Instruments USB-8451 or USB-8452)" +.sp Some programmers have optional or mandatory parameters which are described in detail in the .B PROGRAMMER-SPECIFIC INFORMATION @@ -1075,6 +1077,59 @@ .BR "ch341a_spi " programmer The WCH CH341A programmer does not support any parameters currently. SPI frequency is fixed at 2 MHz, and CS0 is used as per the device. +.SS +.BR "ni845x_spi " programmer +.IP +An optional +.B voltage +parameter could be used to specify the IO voltage. This parameter is available for the NI USB-8452 device. The default unit is +Volt if no unit is specified. You can use +.BR mV ", " milliVolt ", " V " or " Volt +as unit specifier. Syntax is +.sp +.B " flashrom -p ni845x_spi:voltage=value" +.sp +where +.B value +can be +.BR 1.2V ", " 1.5V ", " 1.8V ", " 2.5V ", " 3.3V +or the equivalent in mV. +.sp +You can use the +.B serial +parameter to explicitly specify which connected NI USB-845x device should be used. +You should use your device's 7 digit hexadecimal serial number. +Usage example to select the device with 1230A2 serial number: +.sp +.B " flashrom -p ni845x_spi:serial=1230A2" +.sp +An optional +.B spispeed +parameter specifies the frequency of the SPI bus. +Syntax is +.sp +.B " flashrom -p ni845x_spi:spispeed=frequency" +.sp +where +.B frequency +should a number corresponding to the desired frequency in KHz. +The maximum +.B frequency +is 12 MHz (12000 KHz) for the USB-8451 and 50 MHz (50000 KHz) for the USB-8452. +The default is a frequency of 1 MHz (1000 KHz). +.sp +An optional +.B cs +parameter specifies which target chip select line should be used. Syntax is +.sp +.B " flashrom -p ni845x_spi:csnumber=value" +.sp +where +.B value +should be between +.BR 0 " and " 7 +By default the CS0 is used. +.SS .SH EXAMPLES To back up and update your BIOS, run .sp diff --git a/flashrom.c b/flashrom.c index d87a431..ccb13c0 100644 --- a/flashrom.c +++ b/flashrom.c @@ -405,6 +405,17 @@ }, #endif
+#if CONFIG_NI845X_SPI == 1 + { + .name = "ni845x_spi", + .type = OTHER, // choose other because NI-845x uses own USB implementation + .devs.note = "National Instruments USB-845x\n", + .init = ni845x_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif {0}, /* This entry corresponds to PROGRAMMER_INVALID. */ };
diff --git a/ni845x_spi.c b/ni845x_spi.c new file mode 100644 index 0000000..c8453a6 --- /dev/null +++ b/ni845x_spi.c @@ -0,0 +1,508 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2018 Miklós Márton martonmiklosqdev@gmail.com + * + * 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 + */ +#if CONFIG_NI845X_SPI == 1 + + +#include <ctype.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ni845x.h> +#include <unistd.h> + +#include "flash.h" +#include "programmer.h" +#include "spi.h" + + +enum USB845xType { + USB8451 = 0x7166, + USB8452 = 0x7514, + Unknown_NI845X_Device +}; + +static const struct spi_master spi_programmer_ni845x; + +static unsigned char CS_number = 0; // use Chip select 0 as default +static char *serial_number = NULL; // by default open the first connected device +static enum USB845xType device_pid = Unknown_NI845X_Device; + +static uInt32 device_handle = 0; +static NiHandle configuration_handle = 0; +static char error_string_buffer[1024]; + +// forward declaration +static int ni845x_spi_shutdown(void *data); +static int32 ni845x_spi_open_resource(char *resource_handle, uInt32 *opened_handle); + +// USB-8452 supported voltages, keep this array in descending order! +static uint8_t usb8452_io_voltages_in_100mV[5] = { + kNi845x33Volts, + kNi845x25Volts, + kNi845x18Volts, + kNi845x15Volts, + kNi845x12Volts +}; + +/* Copied from dediprog.c */ +/* Might be useful for other USB devices as well. static for now. */ +static int parse_voltage(char *voltage) +{ + char *tmp = NULL; + int i; + int millivolt = 0, fraction = 0; + + if (!voltage || !strlen(voltage)) { + msg_perr("Empty voltage= specified.\n"); + return -1; + } + millivolt = (int)strtol(voltage, &tmp, 0); + voltage = tmp; + /* Handle "," and "." as decimal point. Everything after it is assumed + * to be in decimal notation. + */ + if ((*voltage == '.') || (*voltage == ',')) { + voltage++; + for (i = 0; i < 3; i++) { + fraction *= 10; + /* Don't advance if the current character is invalid, + * but continue multiplying. + */ + if ((*voltage < '0') || (*voltage > '9')) + continue; + fraction += *voltage - '0'; + voltage++; + } + /* Throw away remaining digits. */ + voltage += strspn(voltage, "0123456789"); + } + /* The remaining string must be empty or "mV" or "V". */ + tolower_string(voltage); + + /* No unit or "V". */ + if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) { + millivolt *= 1000; + millivolt += fraction; + } else if (!strncmp(voltage, "mv", 2) || + !strncmp(voltage, "millivolt", 9)) { + /* No adjustment. fraction is discarded. */ + } else { + /* Garbage at the end of the string. */ + msg_perr("Garbage voltage= specified.\n"); + return -1; + } + return millivolt; +} + +/** + * @brief ni845x_spi_open + * @param serial a null terminated string containing the serial number of the specific device or NULL + * @return the 0 on successful competition, negative error code on failure + */ +static int ni845x_spi_open(char *serial, uInt32 *returnHandle) +{ + char resource_handle[256]; + NiHandle device_find_handle; + uInt32 found_devices_count = 0; + int32 tmp = 0; + uint32_t vid, usb_bus, serial_as_number; + int ret = -1; + + tmp = ni845xFindDevice(resource_handle, &device_find_handle, &found_devices_count); + if (tmp == 0) { + if (found_devices_count) { + // Read the serial number and the PID here + // VISA resource string format example: + // USB0::0x3923::0x7514::DEADBEEF::RAW + // where the 0x7514 is the PID + // and the DEADBEEF is the serial of the device + sscanf(resource_handle, "USB%d::0x%04X::0x%04X::%08X::RAW", + &usb_bus, &vid, &device_pid, &serial_as_number); + if (serial_number == NULL) { + // if no serial number passed -> open the first device + if (ni845x_spi_open_resource(resource_handle, returnHandle) == 0) { + ret = 0; + } + } else { + // serial number parameter specified -> check the resource_handle + // and if it is not the device what we are looking for + // loop on the available devices + do { + if (strtol(serial_number, NULL, 16) == serial_as_number) { + if (ni845x_spi_open_resource(resource_handle, returnHandle) == 0) + ret = 0; + break; + } + + // serial does not match the requested -> continue with the next one + found_devices_count--; + if (found_devices_count) { + if (ni845xFindDeviceNext(device_find_handle, resource_handle) == 0) { + sscanf(resource_handle, "USB%d::0x%04X::0x%04X::%08X::RAW", + &usb_bus, &vid, &device_pid, &serial_as_number); + } + } + } while (found_devices_count); + } + } + } else { + ni845xStatusToString(tmp, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xFindDevice failed with: %s (%ld)\n", error_string_buffer, tmp); + return -1; + } + + tmp = ni845xCloseFindDeviceHandle(device_find_handle); + if (tmp) { + ni845xStatusToString(tmp, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xCloseFindDeviceHandle failed with: %s (%ld)\n", error_string_buffer, tmp); + return -1; + } + return ret; +} + +/** + * @brief ni845x_spi_open_resource + * @param resource_handle the resource handle returned by the ni845xFindDevice or ni845xFindDeviceNext + * @param opened_handle the opened handle from the ni845xOpen + * @return the 0 on successful competition, negative error code on failure positive code on warning + */ +static int32 ni845x_spi_open_resource(char *resource_handle, uInt32 *opened_handle) +{ + int32 ret = ni845xOpen(resource_handle, opened_handle); + if (ret < 0) { + ni845xStatusToString(ret, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xOpen(%s) failed with: %s (%ld)\n", resource_handle, error_string_buffer, ret); + } else if (ret > 0) { + ni845xStatusToString(ret, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_pwarn("ni845xOpen(%s) failed with: %s (%ld)\n", resource_handle, error_string_buffer, ret); + } + return ret; +} + +/** + * @brief usb8452_spi_set_io_voltage sets the IO voltage for the USB-8452 devices + * @param IOVoltage_mV the desired IO voltage in mVolts + * @return 0 on success, negative on error, positive on warning + */ +static int usb8452_spi_set_io_voltage(uint16_t IOVoltage_mV) +{ + int i = 0; + uint8_t selected_voltage_100mV = 0; + uint8_t IO_voltage_100mV = 0; + + if (device_pid == USB8451) { + msg_pwarn("USB-8451 does not supports the changing of the SPI IO voltage\n"); + return 0; + } + + // limit the IO voltage to 3.3V + if (IOVoltage_mV > 3300) { + msg_pinfo("USB-8452 maximum IO voltage is 3.3V\n"); + return -1; + } else { + IO_voltage_100mV = ((float)IOVoltage_mV / 100.0f); + } + + // usb8452_io_voltages_in_100mV is a decreasing list of supported voltages + for (i = 0; i < ARRAY_SIZE(usb8452_io_voltages_in_100mV) - 1; i++) { + if (usb8452_io_voltages_in_100mV[i] >= IO_voltage_100mV && + IO_voltage_100mV > usb8452_io_voltages_in_100mV[i+1]) { + selected_voltage_100mV = usb8452_io_voltages_in_100mV[i]; + if (IO_voltage_100mV != usb8452_io_voltages_in_100mV[i]) { + msg_pwarn("USB-8452 IO Voltage forced to: %.1f V\n", (float)selected_voltage_100mV / 10.0f); + } else { + msg_pwarn("USB-8452 IO Voltage set to: %.1f V\n", (float)selected_voltage_100mV / 10.0f); + } + break; + } + } + + if (selected_voltage_100mV == 0) { + msg_perr("The USB-8452 does not supports the %.1f V IO voltage\n", IOVoltage_mV/1000.0f); + msg_perr("Supported IO voltages: "); + for (i = 0; i < ARRAY_SIZE(usb8452_io_voltages_in_100mV); i++) { + msg_perr("%.1fV", (double)usb8452_io_voltages_in_100mV[i]/1000.0f); + if (i != ARRAY_SIZE(usb8452_io_voltages_in_100mV) - 1) + msg_perr(", "); + } + msg_perr("\n"); + return -1; + } + + i = ni845xSetIoVoltageLevel(device_handle, selected_voltage_100mV); + if (i != 0) { + ni845xStatusToString(i, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSetIoVoltageLevel failed with: %s (%d)\n", error_string_buffer, i); + return -1; + } + return 0; +} + +/** + * @brief ni845x_spi_set_speed sets the SPI SCK speed + * @param clockToSetInkHz SCK speed in KHz + * @return + */ +static int ni845x_spi_set_speed(uint16_t clockToSetInkHz) +{ + int32 i = ni845xSpiConfigurationSetClockRate(configuration_handle, clockToSetInkHz); + uInt16 clock_freq_read_KHz; + if (i != 0) { + ni845xStatusToString(i, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSpiConfigurationSetClockRate(%d) failed with: %s (%ld)\n", + clockToSetInkHz, + error_string_buffer, + i); + return -1; + } + + // read back the clock frequency and notify the user if it is not the same as it was requested + i = ni845xSpiConfigurationGetClockRate(configuration_handle, &clock_freq_read_KHz); + if (i != 0) { + ni845xStatusToString(i, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSpiConfigurationSetClockRate failed with: %s (%ld)\n", error_string_buffer, i); + return -1; + } else { + if (clock_freq_read_KHz != clockToSetInkHz) { + msg_pinfo("SPI clock frequency forced to: %d KHz (requested: %d KHz)\n", + (int)clock_freq_read_KHz, (int)clockToSetInkHz); + } else { + msg_pinfo("SPI clock frequency set to: %d KHz\n", + (int)clockToSetInkHz); + } + return 0; + } +} + +/** + * @brief ni845x_spi_print_available_devices prints a list of the available devices + */ +void ni845x_spi_print_available_devices(void) +{ + char resource_handle[256], device_type_string[16]; + NiHandle device_find_handle; + uInt32 found_devices_count = 0; + int32 tmp = 0; + uint32_t pid, vid, usb_bus, serial_as_number; + + tmp = ni845xFindDevice(resource_handle, &device_find_handle, &found_devices_count); + if (tmp != 0) { + ni845xStatusToString(tmp, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xFindDevice() failed with: %s (%ld)\n", + error_string_buffer, + tmp); + return; + } + + if (found_devices_count) { + msg_pinfo("Available devices:\n"); + do { + sscanf(resource_handle, "USB%d::0x%04X::0x%04X::%08X::RAW", + &usb_bus, &vid, &pid, &serial_as_number); + switch (pid) { + case USB8451: + snprintf(device_type_string, ARRAY_SIZE(device_type_string), "USB-8451"); + break; + case USB8452: + snprintf(device_type_string, ARRAY_SIZE(device_type_string), "USB-8452"); + break; + default: + snprintf(device_type_string, ARRAY_SIZE(device_type_string), "Unknown device"); + break; + } + msg_pinfo("- %X (%s)\n", serial_as_number, device_type_string); + + found_devices_count--; + if (found_devices_count) { + ni845xFindDeviceNext(device_find_handle, resource_handle); + } + } while (found_devices_count); + } + + tmp = ni845xCloseFindDeviceHandle(device_find_handle); + if (tmp) { + ni845xStatusToString(tmp, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xCloseFindDeviceHandle failed with: %s (%ld)\n", error_string_buffer, tmp); + } +} + +int ni845x_spi_init(void) +{ + char *speed_str = NULL; + char *CS_str = NULL; + char *voltage = NULL; + int IO_voltage_mV = 3300; + int spi_speed_KHz = 1000; // selecting 1 MHz SCK is a good bet + int32 tmp = 0; + + // read the cs parameter (which Chip select should we use) + CS_str = extract_programmer_param("cs"); + if (CS_str) { + CS_number = atoi(CS_str); + if (CS_number < 0 || 7 < CS_number) { + msg_perr("Only CS 0-7 supported\n"); + return 1; + } + free(CS_str); + } + + voltage = extract_programmer_param("voltage"); + if (voltage != NULL) { + IO_voltage_mV = parse_voltage(voltage); + free(voltage); + if (IO_voltage_mV < 0) + return 1; + } + + serial_number = extract_programmer_param("serial"); + if (ni845x_spi_open(serial_number, &device_handle)) { + if (serial_number) { + msg_pinfo("Could not find any NI USB-8451/8452 with serialnumber: %s!\n", serial_number); + ni845x_spi_print_available_devices(); + msg_pinfo("Check the S/N field on the bottom of the device,\n" + "or use 'lsusb -v -d 3923:7166 | grep Serial' for USB-8451\n" + "or 'lsusb -v -d 3923:7514 | grep Serial' for USB-8452\n"); + free(serial_number); + } else { + msg_pinfo("Could not find any NI USB-845x device!\n"); + } + return 1; + } else { + // open the SPI config handle + tmp = ni845xSpiConfigurationOpen(&configuration_handle); + if (tmp != 0) { + ni845xStatusToString(tmp, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSpiConfigurationOpen() failed with: %s (%ld)\n", + error_string_buffer, + tmp); + } + } + + if (serial_number) + free(serial_number); + + if (usb8452_spi_set_io_voltage(IO_voltage_mV) < 0) { + return 1; // no alert here usb8452_spi_set_io_voltage already printed that + } + + speed_str = extract_programmer_param("spispeed"); + if (speed_str) { + spi_speed_KHz = atoi(speed_str); + free(speed_str); + } + + if (ni845x_spi_set_speed(spi_speed_KHz)) { + msg_perr("Unable to set SPI speed\n"); + return 1; + } + + if (register_shutdown(ni845x_spi_shutdown, NULL)) + return 1; + register_spi_master(&spi_programmer_ni845x); + + return 0; +} + +static int ni845x_spi_shutdown(void *data) +{ + int32 ret = 0; + if (configuration_handle != 0) { + ret = ni845xSpiConfigurationClose(configuration_handle); + if (ret) { + ni845xStatusToString(ret, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSpiConfigurationClose failed with: %s (%ld)\n", error_string_buffer, ret); + } + } + + if (device_handle != 0) { + ret = ni845xClose(device_handle); + if (ret) { + ni845xStatusToString(ret, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSpiConfigurationClose failed with: %s (%ld)\n", error_string_buffer, ret); + } + } + return 0; +} + +static int ni845x_spi_transmit(struct flashctx *flash, + unsigned int write_cnt, + unsigned int read_cnt, + const unsigned char *write_arr, + unsigned char *read_arr) +{ + uInt32 read_size = 0; + uInt8 *transfer_buffer = NULL; + int32 ret = 0; + + transfer_buffer = calloc(write_cnt + read_cnt, sizeof(uInt8)); + if (transfer_buffer == NULL) { + msg_gerr("Memory allocation failed!\n"); + msg_cinfo("FAILED.\n"); + return -1; + } + + memcpy(transfer_buffer, write_arr, write_cnt); + + ret = ni845xSpiWriteRead(device_handle, + configuration_handle, + (uInt32)(write_cnt + read_cnt), + transfer_buffer, + &read_size, + transfer_buffer); + if (ret < 0) { + // Negative specifies an error, meaning the function did not perform the expected behavior. + ni845xStatusToString(ret, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_perr("ni845xSpiWriteRead failed with: %s (%ld)\n", error_string_buffer, ret); + free(transfer_buffer); + return -1; + } else if (ret > 0) { + // Positive specifies a warning, meaning the function performed as expected, + // but a condition arose that might require attention. + ni845xStatusToString(ret, ARRAY_SIZE(error_string_buffer), error_string_buffer); + msg_pwarn("ni845xSpiWriteRead returned with: %s (%ld)\n", error_string_buffer, ret); + } + + if (read_cnt != 0 && read_arr != NULL) { + if ((read_cnt + write_cnt) != read_size) { + msg_perr("ni845x_spi_transmit: expected and returned read count mismatch: %u expected, %ld recieved\n", + read_cnt, read_size); + free(transfer_buffer); + return -1; + } + memcpy(read_arr, &transfer_buffer[write_cnt], read_cnt); + } + free(transfer_buffer); + return 0; +} + + +static const struct spi_master spi_programmer_ni845x = { + .type = SPI_CONTROLLER_NI845X, + .max_data_read = MAX_DATA_READ_UNLIMITED, + .max_data_write = MAX_DATA_WRITE_UNLIMITED, + .command = ni845x_spi_transmit, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = default_spi_write_256, + .write_aai = default_spi_write_aai, +}; + + +#endif // endif CONFIG_NI845X diff --git a/programmer.h b/programmer.h index 139f4fa..96adeb6 100644 --- a/programmer.h +++ b/programmer.h @@ -113,6 +113,9 @@ #if CONFIG_CH341A_SPI == 1 PROGRAMMER_CH341A_SPI, #endif +#if CONFIG_NI845X_SPI == 1 + PROGRAMMER_NI845X_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -545,6 +548,11 @@ extern const struct dev_entry devs_ch341a_spi[]; #endif
+/* ni845x_spi.c */ +#if CONFIG_NI845X_SPI == 1 +int ni845x_spi_init(void); +#endif + /* flashrom.c */ struct decode_sizes { uint32_t parallel; @@ -607,6 +615,9 @@ #if CONFIG_CH341A_SPI == 1 SPI_CONTROLLER_CH341A_SPI, #endif +#if CONFIG_NI845X_SPI == 1 + SPI_CONTROLLER_NI845X, +#endif };
#define MAX_DATA_UNSPECIFIED 0