Просмотр исходного кода

gblink_pinconf: add save/load configurations and helper funcs

The pin get/set functions have _by_gpiopin and _by_pinnum variants
with macros linking the original functions to _by_gpiopin

Signed-off-by: Kris Bahnsen <Kris@KBEmbedded.com>
Kris Bahnsen 1 год назад
Родитель
Сommit
ecce5b6363

+ 300 - 5
gblink/gblink_pinconf.c

@@ -2,13 +2,26 @@
 // Copyright (c) 2023 KBEmbedded
 // Copyright (c) 2023 KBEmbedded
 
 
 #include <furi.h>
 #include <furi.h>
-#include <furi_hal.h>
+#include <storage/storage.h>
+#include <lib/toolbox/stream/stream.h>
+#include <lib/flipper_format/flipper_format.h>
 
 
 #include <stdint.h>
 #include <stdint.h>
 
 
 #include <gblink/include/gblink.h>
 #include <gblink/include/gblink.h>
 #include "gblink_i.h"
 #include "gblink_i.h"
 
 
+#define PINCONF_FILE_TYPE	"Flipper GB Link Pinconf"
+#define PINCONF_FILE_VER	1
+
+#define PINCONF_ORIG		"Original"
+#define PINCONF_MLVK		"MLVK2.5"
+#define PINCONF_CUST		"Custom"
+
+#define PINCONF_SI		"SI"
+#define PINCONF_SO		"SO"
+#define PINCONF_CLK		"CLK"
+
 struct gblink_pins {
 struct gblink_pins {
         const GpioPin *serin;
         const GpioPin *serin;
         const GpioPin *serout;
         const GpioPin *serout;
@@ -33,13 +46,14 @@ const struct gblink_pins common_pinouts[PINOUT_COUNT] = {
 	},
 	},
 };
 };
 
 
-int gblink_pin_set(void *handle, gblink_bus_pins pin, const GpioPin *gpio)
+
+int gblink_pin_set_by_gpiopin(void *handle, gblink_bus_pins pin, const GpioPin *gpio)
 {
 {
 	furi_assert(handle);
 	furi_assert(handle);
 	struct gblink *gblink = handle;
 	struct gblink *gblink = handle;
 
 
 	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
 	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
-		return 1;
+		return -1;
 
 
 	switch (pin) {
 	switch (pin) {
 	case PIN_SERIN:
 	case PIN_SERIN:
@@ -64,7 +78,7 @@ int gblink_pin_set(void *handle, gblink_bus_pins pin, const GpioPin *gpio)
 	return 0;
 	return 0;
 }
 }
 
 
-const GpioPin *gblink_pin_get(void *handle, gblink_bus_pins pin)
+const GpioPin *gblink_pin_get_by_gpiopin(void *handle, gblink_bus_pins pin)
 {
 {
 	furi_assert(handle);
 	furi_assert(handle);
 	struct gblink *gblink = handle;
 	struct gblink *gblink = handle;
@@ -91,8 +105,11 @@ int gblink_pin_set_default(void *handle, gblink_pinouts pinout)
 	furi_assert(handle);
 	furi_assert(handle);
 	struct gblink *gblink = handle;
 	struct gblink *gblink = handle;
 
 
+	if (pinout == PINOUT_CUSTOM || pinout >= PINOUT_COUNT)
+		return -1;
+
 	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
 	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
-		return 1;
+		return -1;
 
 
 	gblink->serin = common_pinouts[pinout].serin;
 	gblink->serin = common_pinouts[pinout].serin;
 	gblink->serout = common_pinouts[pinout].serout;
 	gblink->serout = common_pinouts[pinout].serout;
@@ -127,3 +144,281 @@ int gblink_pin_get_default(void *handle)
 
 
 	return i;
 	return i;
 }
 }
+
+int gblink_pin_set(void *handle, gblink_bus_pins pin, unsigned int pinnum)
+{
+	furi_assert(handle);
+	furi_assert(pinnum < gpio_pins_count);
+
+	struct gblink *gblink = handle;
+
+	if (furi_mutex_acquire(gblink->start_mutex, 0) != FuriStatusOk)
+		return -1;
+
+	switch (pin) {
+	case PIN_SERIN:
+		gblink->serin = gpio_pins[pinnum].pin;
+		break;
+	case PIN_SEROUT:
+		gblink->serout = gpio_pins[pinnum].pin;
+		break;
+	case PIN_CLK:
+		gblink->clk = gpio_pins[pinnum].pin;
+		break;
+	case PIN_SD:
+		gblink->sd = gpio_pins[pinnum].pin;
+		break;
+	default:
+		furi_crash();
+		break;
+	}
+
+	furi_mutex_release(gblink->start_mutex);
+
+	return 0;
+}
+
+int gblink_pin_get(void *handle, gblink_bus_pins pin)
+{
+	furi_assert(handle);
+	struct gblink *gblink = handle;
+	unsigned int i;
+
+	for (i = 0; i < gpio_pins_count; i++) {
+		switch (pin) {
+		case PIN_SERIN:
+			if (gpio_pins[i].pin == gblink->serin)
+				return i;
+			break;
+		case PIN_SEROUT:
+			if (gpio_pins[i].pin == gblink->serout)
+				return i;
+			break;
+		case PIN_CLK:
+			if (gpio_pins[i].pin == gblink->clk)
+				return i;
+			break;
+		case PIN_SD:
+			if (gpio_pins[i].pin == gblink->sd)
+				return i;
+			break;
+		default:
+			furi_crash();
+			break;
+		}
+	}
+
+	return -1;
+}
+
+int gblink_pin_count_max(void)
+{
+	unsigned int i;
+	int count = 0;
+
+	for (i = 0; i < gpio_pins_count; i++)
+		if (!gpio_pins[i].debug)
+			count = i;
+
+	return count;
+}
+
+int gblink_pin_get_next(unsigned int pinnum)
+{
+	unsigned int i;
+
+	/* The check could be eliminated and just rely on the return -1 at the
+	 * end of the function, but this is a shortcut so we don't have to walk
+	 * through the whole table just to find out its an out-of-bounds pin.
+	 */
+	if (pinnum >= gpio_pins_count)
+		return -1;
+
+	for (i = pinnum; i < gpio_pins_count; i++)
+		if (!gpio_pins[i].debug)
+			return i;
+
+	return -1;
+}
+
+int gblink_pin_get_prev(unsigned int pinnum)
+{
+	int i;
+
+	/* The check could be eliminated and just rely on the return -1 at the
+	 * end of the function, but this is a shortcut so we don't have to walk
+	 * through the whole table just to find out its an out-of-bounds pin.
+	 */
+	if (pinnum >= gpio_pins_count)
+		return -1;
+
+	for (i = pinnum; i >= 0; i--)
+		if (!gpio_pins[i].debug)
+			return i;
+
+	return -1;
+}
+
+bool gblink_pinconf_load(void *gblink)
+{
+
+	Storage *storage = NULL;;
+	FlipperFormat *data_file = NULL;
+	FuriString *string = NULL;
+	uint32_t val;
+	bool ret = false;
+
+	storage = furi_record_open(RECORD_STORAGE);
+
+	string = furi_string_alloc_set(APP_DATA_PATH(""));
+	storage_common_resolve_path_and_ensure_app_directory(storage, string);
+	furi_string_cat_str(string, ".gblink_pinconf");
+
+	data_file = flipper_format_file_alloc(storage);
+
+	if (!flipper_format_file_open_existing(data_file, furi_string_get_cstr(string))) {
+		FURI_LOG_E("pinconf", "Error opening file %s", furi_string_get_cstr(string));
+		goto out;
+	}
+
+	if (!flipper_format_read_header(data_file, string, &val)) {
+		FURI_LOG_E("pinconf", "Missing or incorrect header");
+		goto out;
+	}
+
+	if (strncmp(furi_string_get_cstr(string), PINCONF_FILE_TYPE, strlen(PINCONF_FILE_TYPE)) ||
+	    val != PINCONF_FILE_VER) {
+		FURI_LOG_E("pinconf", "Type or version mismatch");
+		goto out;
+	}
+
+	if (!flipper_format_read_string(data_file, "Mode", string)) {
+		FURI_LOG_E("pinconf", "Missing Mode");
+		goto out;
+	}
+
+	if (!strncmp(furi_string_get_cstr(string), PINCONF_ORIG, strlen(PINCONF_ORIG))) {
+		FURI_LOG_I("pinconf", "Setting Original pinout");
+		gblink_pin_set_default(gblink, PINOUT_ORIGINAL);
+		goto out;
+	}
+
+	if (!strncmp(furi_string_get_cstr(string), PINCONF_MLVK, strlen(PINCONF_MLVK))) {
+		FURI_LOG_I("pinconf", "Setting MALVEKE 2.5 pinout");
+		gblink_pin_set_default(gblink, PINOUT_MALVEKE_EXT1);
+		goto out;
+	}
+
+	if (!strncmp(furi_string_get_cstr(string), PINCONF_CUST, strlen(PINCONF_CUST))) {
+		FURI_LOG_I("pinconf", "Setting Custom pinout");
+	}
+
+	if (!flipper_format_read_uint32(data_file, PINCONF_SI, &val, 1)) {
+		FURI_LOG_E("pinconf", "Missing SI");
+		goto out;
+	} else {
+		gblink_pin_set(gblink, PIN_SERIN, val);
+	}
+
+	if (!flipper_format_read_uint32(data_file, PINCONF_SO, &val, 1)) {
+		FURI_LOG_E("pinconf", "Missing SO");
+		goto out;
+	} else {
+		gblink_pin_set(gblink, PIN_SEROUT, val);
+	}
+
+	if (!flipper_format_read_uint32(data_file, PINCONF_CLK, &val, 1)) {
+		FURI_LOG_E("pinconf", "Missing CLK");
+		goto out;
+	} else {
+		gblink_pin_set(gblink, PIN_CLK, val);
+	}
+
+	ret = true;
+
+out:
+	flipper_format_file_close(data_file);
+	furi_string_free(string);
+	furi_record_close(RECORD_STORAGE);
+	return ret;
+}
+
+/* XXX: TODO: I think there is a way to build this ahead of time and
+ * write it in one go to be a bit more time efficient.
+ */
+bool gblink_pinconf_save(void *gblink)
+{
+
+	Storage *storage = NULL;;
+	FlipperFormat *data_file = NULL;
+	FuriString *string = NULL;
+	int rc;
+	uint32_t pin;
+	bool ret = false;
+
+	storage = furi_record_open(RECORD_STORAGE);
+
+	string = furi_string_alloc_set(APP_DATA_PATH(""));
+	storage_common_resolve_path_and_ensure_app_directory(storage, string);
+	furi_string_cat_str(string, ".gblink_pinconf");
+
+	data_file = flipper_format_file_alloc(storage);
+
+	if (!flipper_format_file_open_always(data_file, furi_string_get_cstr(string))) {
+		FURI_LOG_E("pinconf", "Error opening file %s", furi_string_get_cstr(string));
+		goto out;
+	}
+
+	if (!flipper_format_write_header_cstr(data_file, PINCONF_FILE_TYPE, PINCONF_FILE_VER)) {
+		FURI_LOG_E("pinconf", "Error writing header to file");
+		goto out;
+	}
+
+	rc = gblink_pin_get_default(gblink);
+	switch (rc) {
+	case 0:
+		if (!flipper_format_write_string_cstr(data_file, "Mode", PINCONF_ORIG))
+			FURI_LOG_E("pinconf", "Error writing mode to file");
+		goto out;
+		break;
+	case 1:
+		if (!flipper_format_write_string_cstr(data_file, "Mode", PINCONF_MLVK))
+			FURI_LOG_E("pinconf", "Error writing mode to file");
+		goto out;
+		break;
+	case -1:
+		if (!flipper_format_write_string_cstr(data_file, "Mode", PINCONF_CUST))
+			FURI_LOG_E("pinconf", "Error writing mode to file");
+		break;
+	default:
+		FURI_LOG_E("pinconf", "Unknown mode");
+		goto out;
+		break;
+	}
+
+	pin = gblink_pin_get(gblink, PIN_SERIN);
+	if (!flipper_format_write_uint32(data_file, "SI", &pin, 1)) {
+		FURI_LOG_E("pinconf", "Error writing SI to file");
+		goto out;
+	}
+
+	pin = gblink_pin_get(gblink, PIN_SEROUT);
+	if (!flipper_format_write_uint32(data_file, "SO", &pin, 1)) {
+		FURI_LOG_E("pinconf", "Error writing SO to file");
+		goto out;
+	}
+
+	pin = gblink_pin_get(gblink, PIN_CLK);
+	if (!flipper_format_write_uint32(data_file, "CLK", &pin, 1)) {
+		FURI_LOG_E("pinconf", "Error writing CLK to file");
+	}
+
+	ret = true;
+
+out:
+	flipper_format_file_close(data_file);
+	furi_string_free(string);
+	furi_record_close(RECORD_STORAGE);
+	return ret;
+}
+

+ 2 - 1
gblink/include/gblink.h

@@ -49,7 +49,8 @@ typedef enum {
 } gblink_speed;
 } gblink_speed;
 
 
 typedef enum {
 typedef enum {
-	PINOUT_ORIGINAL,
+	PINOUT_CUSTOM = -1,
+	PINOUT_ORIGINAL = 0,
 	PINOUT_MALVEKE_EXT1,
 	PINOUT_MALVEKE_EXT1,
 	PINOUT_COUNT,
 	PINOUT_COUNT,
 } gblink_pinouts;
 } gblink_pinouts;

+ 94 - 6
gblink/include/gblink_pinconf.h

@@ -21,7 +21,7 @@ extern "C" {
  *
  *
  * @note The gblink instance must not be gblink_start()'ed!
  * @note The gblink instance must not be gblink_start()'ed!
  *
  *
- * @returns 0 on success, 1 if gblink instance is not gblink_stop()'ed.
+ * @returns 0 on success, -1 if gblink instance is not gblink_stop()'ed.
  */
  */
 int gblink_pin_set_default(void *handle, gblink_pinouts pinout);
 int gblink_pin_set_default(void *handle, gblink_pinouts pinout);
 
 
@@ -35,27 +35,115 @@ int gblink_pin_set_default(void *handle, gblink_pinouts pinout);
 int gblink_pin_get_default(void *handle);
 int gblink_pin_get_default(void *handle);
 
 
 /**
 /**
- * Set a gpio pin to a specific pin mode
+ * Set a GPIO pin to a specific pin mode for the EXT interface
+ *
+ * @param handle Pointer to gblink handle
+ * @param pin Pin mode to assign to the gpio pin
+ * @param pinnum GPIO pin number to assign pin mode to
+ *
+ * @note The gblink instance must not be gblink_start()'ed!
+ *
+ * @returns 0 on success, -1 if gblink instance is not gblink_stop()ed
+ */
+int gblink_pin_set(void *handle, gblink_bus_pins pin, unsigned int pinum);
+
+/**
+ * Get the pin number associated with the requested pin mode
+ *
+ *
+ * @param handle Pointer to gblink handle
+ * @param pin Pin mode to inquire about
+ *
+ * @returns Pin number of the requested pin or -1 on error
+ */
+int gblink_pin_get(void *handle, gblink_bus_pins pin);
+
+/**
+ * Set a gpio pin to a specific pin mode via GpioPin
  *
  *
  * @param handle Pointer to gblink handle
  * @param handle Pointer to gblink handle
  * @param pin Pin mode to assign to the gpio pin
  * @param pin Pin mode to assign to the gpio pin
  * @param gpio Which gpio pin to assign the pin mode
  * @param gpio Which gpio pin to assign the pin mode
  *
  *
  * @note The gblink instance must not be gblink_start()'ed!
  * @note The gblink instance must not be gblink_start()'ed!
+ * @note There is no sane way to do bounds checking with this function, so be
+ * careful when passing pointers to ensure they are actually Flipper GpioPin
+ * references.
  *
  *
- * @returns 0 on success, 1 if gblink instance is not gblink_stop()'ed.
+ * @returns 0 on success, -1 if gblink instance is not gblink_stop()'ed.
  */
  */
-int gblink_pin_set(void *handle, gblink_bus_pins pin, const GpioPin *gpio);
+int gblink_pin_set_by_gpiopin(void *handle, gblink_bus_pins pin, const GpioPin *gpio);
 
 
 /**
 /**
- * Get the gpio pin associated with the requested pin mode
+ * Get the GpioPin associated with the requested pin mode
  *
  *
  * @param handle Pointer to gblink handle
  * @param handle Pointer to gblink handle
  * @param pin Pin mode to inquire about
  * @param pin Pin mode to inquire about
  *
  *
  * @returns GpioPin pointer
  * @returns GpioPin pointer
  */
  */
-const GpioPin *gblink_pin_get(void *handle, gblink_bus_pins pin);
+const GpioPin *gblink_pin_get_by_gpiopin(void *handle, gblink_bus_pins pin);
+
+/**
+ * Returns the pinnum of the highest usable, non-debug pin.
+ *
+ * @returns count of highest usable, non-debug pin or -1 on error
+ */
+int gblink_pin_count_max(void);
+
+/**
+ * Get the next usable, non-debug GPIO pin by number.
+ *
+ * The return value is the next usable, non-debug pin starting from pinnum and
+ * will include pinnum if it is a valid pin.
+ *
+ * @param pinnum GPIO pin number to check if it is a valid pin.
+ *
+ * @returns -1 on error or no more valid pins. Any other value is the next usable
+ * pin which can include pinnum if it is usable. If pinnum is not usable, the next
+ * numbered pin which is will be returned or -1 if there are no further pins.
+ */
+int gblink_pin_get_next(unsigned int pinnum);
+
+/**
+ * Get the previous usable, non-debug GPIO pin by number.
+ *
+ * The return value is the previous usable, non-debug pin starting from pinnum and
+ * will include pinnum if it is a valid pin.
+ *
+ * @param pinnum GPIO pin number to check if it is a valid pin.
+ *
+ * @returns -1 on error or no more valid pins. Any other value is the previous usable
+ * pin which can include pinnum if it is usable. If pinnum is not usable, the previous
+ * numbered pin which is will be returned or -1 if there are no further pins.
+ */
+int gblink_pin_get_prev(unsigned int pinnum);
+
+/**
+ * Load pin configuration from file automatically
+ * Loads from .gblink_pinconf in app data folder
+ *
+ * @param gblink Pointer to gblink instance to set pins configurations
+ *
+ * @returns true on success, false on error
+ *
+ * @note This function should be called from the context of the application and
+ * not any other threads as it uses the app data folder to load data from
+ */
+bool gblink_pinconf_load(void *gblink);
+
+/**
+ * Save current pin configuration to file automatically
+ * Saves to .gblink_pinconf in app data folder
+ *
+ * @param gblink Pointer to gblink instance to save pins configurations
+ *
+ * @returns true on success, false on error
+ *
+ * @note This function should be called from the context of the application and
+ * not any other threads as it uses the app data folder to save data to.
+ */
+bool gblink_pinconf_save(void *gblink);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 11 - 34
protocols/printer/include/printer_proto.h

@@ -91,50 +91,27 @@ void printer_callback_context_set(void *printer_handle, void *context);
  */
  */
 void printer_callback_set(void *printer_handle, void (*callback)(void *context, struct gb_image *image, enum cb_reason reason));
 void printer_callback_set(void *printer_handle, void (*callback)(void *context, struct gb_image *image, enum cb_reason reason));
 
 
-/* Can only be run after alloc, before start */
 /**
 /**
- * Set one of the pre-configured pinouts
- *
- * @param printer_handle Printer instance handle
- * @param pinout Which pinout to use
- *
- * @note The printer instance must not be actively sending or receiving
- *
- * @returns 0 on success, 1 if gblink instance is not gblink_stop()'ed.
- */
-int printer_pin_set_default(void *printer_handle, gblink_pinouts pinout);
-
-/**
- * Set a gpio pin to a specific pin mode
- *
- * @param printer_handle Printer instance handle
- * @param pin Pin mode to assign to the gpio pin
- * @param gpio Which gpio pin to assign the pin mode
- *
- * @note The printer instance must not be actively sending or receiving
+ * Stop a printer instance
  *
  *
- * @returns 0 on success, 1 if gblink instance is not gblink_stop()'ed.
- */
-int printer_pin_set(void *printer_handle, gblink_bus_pins pin, const GpioPin *gpio);
-
-/**
- * Get the gpio pin associated with the requested pin mode
+ * Disables interrupts, stops any pending timers, and enters back to an idle
+ * state. Once called, re-allows changes to be made.
  *
  *
  * @param printer_handle Printer instance handle
  * @param printer_handle Printer instance handle
- * @param pin Pin mode to inquire about
- *
- * @returns GpioPin pointer
  */
  */
-const GpioPin *printer_pin_get(void *printer_handle, gblink_bus_pins pin);
+void printer_stop(void *printer_handle);
 
 
 /**
 /**
- * Stop a printer instance
+ * Get the gblink handle associated with the printer instance.
  *
  *
- * Disables interrupts, stops any pending timers, and enters back to an idle
- * state. Once called, re-allows changes to be made.
+ * @warning It is not recommended to use this to change any gblink variables other
+ * than pin settings! Changing any other settings such as mode, speed, timeout,
+ * callback, etc., can break printer communication.
  *
  *
  * @param printer_handle Printer instance handle
  * @param printer_handle Printer instance handle
+ *
+ * @returns Pointer that can be used with gblink calls directly
  */
  */
-void printer_stop(void *printer_handle);
+void *printer_gblink_handle_get(void *printer_handle);
 
 
 #endif // PRINTER_PROTO_H
 #endif // PRINTER_PROTO_H

+ 4 - 14
protocols/printer/printer_proto.c

@@ -4,6 +4,7 @@
 #include <furi.h>
 #include <furi.h>
 
 
 #include <gblink/include/gblink.h>
 #include <gblink/include/gblink.h>
+#include <gblink/include/gblink_pinconf.h>
 #include <protocols/printer/include/printer_proto.h>
 #include <protocols/printer/include/printer_proto.h>
 #include "printer_i.h"
 #include "printer_i.h"
 
 
@@ -54,6 +55,7 @@ void *printer_alloc(void)
 	printer->image = malloc(sizeof(struct gb_image));
 	printer->image = malloc(sizeof(struct gb_image));
 
 
 	printer->gblink_handle = gblink_alloc();
 	printer->gblink_handle = gblink_alloc();
+	gblink_pinconf_load(printer->gblink_handle);
 
 
 	/* Set up some settings for the print protocol. The final send/receive() calls
 	/* Set up some settings for the print protocol. The final send/receive() calls
 	 * may clobber some of these, but that is intentional and they don't need to
 	 * may clobber some of these, but that is intentional and they don't need to
@@ -96,25 +98,13 @@ void printer_callback_set(void *printer_handle, void (*callback)(void *context,
 	printer->callback = callback;
 	printer->callback = callback;
 }
 }
 
 
-int printer_pin_set_default(void *printer_handle, gblink_pinouts pinout)
+void *printer_gblink_handle_get(void *printer_handle)
 {
 {
 	struct printer_proto *printer = printer_handle;
 	struct printer_proto *printer = printer_handle;
-	return gblink_pin_set_default(printer->gblink_handle, pinout);
-}
-
-int printer_pin_set(void *printer_handle, gblink_bus_pins pin, const GpioPin *gpio)
-{
-	struct printer_proto *printer = printer_handle;
-	return gblink_pin_set(printer->gblink_handle, pin, gpio);
-}
 
 
-const GpioPin *printer_pin_get(void *printer_handle, gblink_bus_pins pin)
-{
-	struct printer_proto *printer = printer_handle;
-	return gblink_pin_get(printer->gblink_handle, pin);
+	return printer->gblink_handle;
 }
 }
 
 
-
 void printer_stop(void *printer_handle)
 void printer_stop(void *printer_handle)
 {
 {
 	struct printer_proto *printer = printer_handle;
 	struct printer_proto *printer = printer_handle;