dediprog: Read device string early

To select one of multiple Dediprogs by `id', we need to know early what
type of device we found, so we can select the proper way to read its id.
OTOH, when a specific device is selected but incompatible (e.g. unknown
firmware version),  we still want to provide correct console output. If
we split the device-type detection from further processing, we can tell
first that a matching device was found and still bail out later in case
the device turns out to be incompatible.

Tested with "SF600PG2. V:01.01.012 HW:01.00", "SF100   V:5.1.9".

Change-Id: I820982d61831e6cd6a830f915dc745a1adea3776
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/102
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/dediprog.c b/dediprog.c
index 52161f1..5debf32 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -161,6 +161,7 @@
 	int in_endpoint;
 	int out_endpoint;
 	int firmwareversion;
+	char devicestring[32+1];
 	enum dediprog_devtype devicetype;
 };
 
@@ -819,10 +820,10 @@
 	return 0;
 }
 
-static int dediprog_check_devicestring(struct dediprog_data *dp_data)
+static int dediprog_read_devicestring(struct dediprog_data *dp_data)
 {
-	const int devstr_len = 32, old_devstr_len = 16;
-	char buf[devstr_len + 1];
+	const int devstr_len = sizeof(dp_data->devicestring) - 1, old_devstr_len = 16;
+	char *const buf = dp_data->devicestring;
 	int ret;
 
 	/* Command Receive Device String. */
@@ -843,13 +844,18 @@
 		dp_data->devicetype = DEV_SF600;
 	else if (memcmp(buf, "SF700", 5) == 0)
 		dp_data->devicetype = DEV_SF700;
-	else {
-		msg_perr("Device not a SF100, SF200, SF600(Plus(-G2)), or SF700!\n");
+	else
 		return 1;
-	}
 
+	return 0;
+}
+
+static int dediprog_check_devicestring(struct dediprog_data *dp_data)
+{
+	char *const buf = dp_data->devicestring;
 	unsigned int sfnum;
 	unsigned int fw[3];
+
 	if (sscanf(buf, "SF%u", &sfnum) != 1 ||
 	    sfnum != dp_data->devicetype / 100 * 100 ||
 	    sscanf(buf, "SF%*s V:%u.%u.%u ", &fw[0], &fw[1], &fw[2]) != 3) {
@@ -1061,7 +1067,7 @@
 /*
  * Open a dediprog_handle with the USB device at the given index.
  * @index   index of the USB device
- * @return  0 for success, -1 for error, -2 for busy device
+ * @return  0 for success, -1 for error, -2 for busy device, -3 for unknown device
  */
 static int dediprog_open(int index, struct dediprog_data *dp_data)
 {
@@ -1089,7 +1095,23 @@
 		libusb_close(dp_data->handle);
 		return -2;
 	}
+
+	/* Try reading the devicestring. If that fails and the device is old
+	   (FW < 6.0.0, which we cannot know),  then we need to try the "set
+	   voltage" command and then attempt to read the devicestring again. */
+	if (dediprog_read_devicestring(dp_data)) {
+		if (dediprog_set_voltage(dp_data->handle))
+			goto unknown_dev;
+		if (dediprog_read_devicestring(dp_data))
+			goto unknown_dev;
+	}
 	return 0;
+
+unknown_dev:
+	msg_pwarn("Ignoring unknown Dediprog device. Not a SF100, SF200, SF600(Plus(G2)), or SF700!\n");
+	libusb_release_interface(dp_data->handle, 0);
+	libusb_close(dp_data->handle);
+	return -3;
 }
 
 static int dediprog_shutdown(void *data)
@@ -1257,8 +1279,8 @@
 			if (ret == -1) {
 				/* no dev */
 				goto init_err_exit;
-			} else if (ret == -2) {
-				/* busy dev */
+			} else if (ret < 0) {
+				/* busy or unknown dev */
 				continue;
 			}
 
@@ -1295,14 +1317,8 @@
 		msg_pinfo("Using dediprog id SF%06d.\n", found_id);
 	}
 
-	/* Try reading the devicestring. If that fails and the device is old (FW < 6.0.0, which we can not know)
-	 * then we need to try the "set voltage" command and then attempt to read the devicestring again. */
-	if (dediprog_check_devicestring(dp_data)) {
-		if (dediprog_set_voltage(dp_data->handle))
-			goto init_err_cleanup_exit;
-		if (dediprog_check_devicestring(dp_data))
-			goto init_err_cleanup_exit;
-	}
+	if (dediprog_check_devicestring(dp_data))
+		goto init_err_cleanup_exit;
 
 	/* SF100/SF200 uses one in/out endpoint, SF600 uses separate in/out endpoints */
 	dp_data->in_endpoint = 2;