commit d294eb5fd63dba16487fbb20d8603d97cbe78691
parent 35602793a52e494da3b52f3c4167467fb24a877b
Author: amin <dev@aminmesbah.com>
Date: Sun, 29 May 2022 06:09:13 +0000
Rewrite main loop as a state machine
FossilOrigin-Name: 04eccf37524b16c78111a89006df45cfa1963290e91f15cb8731b3e10914a2ef
Diffstat:
M | main.c | | | 303 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
1 file changed, 193 insertions(+), 110 deletions(-)
diff --git a/main.c b/main.c
@@ -17,6 +17,42 @@
// https://www.kernel.org/doc/Documentation/hid/hidraw.txt
#include <linux/hidraw.h>
+#define UNREACHABLE assert(false)
+
+enum State
+{
+ DEVICE_HANDLE_INACCESSIBLE,
+ DEVICE_HANDLE_ACCESSIBLE,
+ DEVICE_HANDLE_OPEN,
+ DEVICE_SENDING_DATA,
+ FATAL_ERROR,
+};
+
+void print_state(enum State state)
+{
+ switch (state)
+ {
+ case DEVICE_HANDLE_INACCESSIBLE:
+ printf("DEVICE_HANDLE_INACCESSIBLE\n");
+ break;
+ case DEVICE_HANDLE_ACCESSIBLE:
+ printf("DEVICE_HANDLE_ACCESSIBLE\n");
+ break;
+ case DEVICE_HANDLE_OPEN:
+ printf("DEVICE_HANDLE_OPEN\n");
+ break;
+ case DEVICE_SENDING_DATA:
+ printf("DEVICE_SENDING_DATA\n");
+ break;
+ case FATAL_ERROR:
+ printf("FATAL_ERROR\n");
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
volatile static bool running = true;
static void stop_running(int signal)
@@ -43,149 +79,196 @@ int main(void)
}
}
-
// NOTE: Included udev rules create this symlink to the appropriate hidraw
// entry.
const char *hid_file_path = "/dev/co2mini0";
const char *temperature_file_path = "/tmp/co2minimon_temp";
const char *co2_file_path = "/tmp/co2minimon_co2";
- bool error_occurred = false;
+ enum State state = DEVICE_HANDLE_INACCESSIBLE;
int device_handle = -1;
- bool need_send_feature_report = true;
while (running)
{
- if (access(hid_file_path, F_OK) != 0)
+ print_state(state);
+ switch (state)
{
- if (close(device_handle) == 0)
+ case DEVICE_HANDLE_INACCESSIBLE:
{
- device_handle = -1;
+ if (access(hid_file_path, F_OK) == 0)
+ {
+ state = DEVICE_HANDLE_ACCESSIBLE;
+ }
+ else
+ {
+ sleep(30);
+ }
}
-
- unlink(co2_file_path);
- unlink(temperature_file_path);
-
- sleep(30);
- continue;
- }
-
- if (device_handle == -1)
- {
- device_handle = open(hid_file_path, O_RDWR);
- if (device_handle < 0)
+ break; case DEVICE_HANDLE_ACCESSIBLE:
{
- printf("ERROR: Failed to open HID.\n");
- goto error;
+ errno = 0;
+ device_handle = open(hid_file_path, O_RDWR);
+ if (device_handle >= 0)
+ {
+ state = DEVICE_HANDLE_OPEN;
+ }
+ else if (errno == ENOENT)
+ {
+ state = DEVICE_HANDLE_INACCESSIBLE;
+ }
+ else if (errno != EINTR)
+ {
+ printf("ERROR: Failed to open HID.\n");
+ state = FATAL_ERROR;
+ }
}
-
- need_send_feature_report = true;
- }
-
- if (need_send_feature_report) {
- uint8_t key[8] = {0};
- int result = ioctl(device_handle, HIDIOCSFEATURE(sizeof(key)), key);
- if (result < 0 || result != sizeof(key))
+ break; case DEVICE_HANDLE_OPEN:
{
- printf("ERROR: Failed to send feature report.\n");
- goto error;
+ uint8_t key[8] = {0};
+ errno = 0;
+ int result = ioctl(device_handle, HIDIOCSFEATURE(sizeof(key)), key);
+ if (result == sizeof(key))
+ {
+ state = DEVICE_SENDING_DATA;
+ }
+ else if (result < 0)
+ {
+ if (errno == ENODEV)
+ {
+ // TODO: do we need to do this in any other cases?
+ close(device_handle);
+ unlink(co2_file_path);
+ unlink(temperature_file_path);
+ state = DEVICE_HANDLE_ACCESSIBLE;
+ }
+ else if (errno != EINTR)
+ {
+ printf("ERROR: Failed to send feature report.\n");
+ state = FATAL_ERROR;
+ }
+ }
+ else
+ {
+ // TODO: when does this happen
+ printf("ERROR: Failed to send feature report.\n");
+ state = FATAL_ERROR;
+ }
}
- need_send_feature_report = false;
- }
-
- fd_set read_fds;
- FD_ZERO(&read_fds);
- FD_SET(device_handle, &read_fds);
- errno = 0;
- int ready = select(device_handle + 1, &read_fds, NULL, NULL, &(struct timeval){ .tv_sec = 15 });
- if (ready == -1 && errno != EINTR)
- {
- printf("ERROR: Failed to select() device handle.\n");
- goto error;
- }
- else if (ready == 0)
- {
- // select() timed out
- //
- // If we go too long with an empty pipe, the device may have
- // stopped sending data. One situation where I've observed this can
- // happen is when the system resumes from hibernation. In this
- // case, all calls to read() will block until we resend the feature
- // report.
- //
- // I'm not sure why this happens, but my guess would be that the
- // device stops sending new data if the data hasn't been read in a
- // while.
- need_send_feature_report = true;
- }
- else
- {
- uint8_t data[8] = {0};
- int bytes_read = read(device_handle, &data, sizeof(data));
-
- uint8_t item = data[0];
- uint8_t msb = data[1];
- uint8_t lsb = data[2];
- uint8_t checksum = data[3];
- uint8_t end = data[4];
-
- if (bytes_read == sizeof(data)
- && end == 0x0d
- && (item == 0x42 || item == 0x50)
- && ((item + msb + lsb) & 0xFF) == checksum)
+ break; case DEVICE_SENDING_DATA:
{
- need_send_feature_report = false;
- uint16_t value = (((uint16_t)msb) << 8) | lsb;
- char buf[1024] = {0};
- int str_len = 0;
- mode_t create_mode = S_IRUSR | S_IWUSR;
- int open_mode = O_WRONLY | O_CREAT | O_TRUNC;
-
- switch(item)
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ FD_SET(device_handle, &read_fds);
+ errno = 0;
+ int ready = select(device_handle + 1, &read_fds, NULL, NULL, &(struct timeval){ .tv_sec = 15 });
+ if (ready == -1)
+ {
+ if (errno != EINTR)
+ {
+ printf("ERROR: Failed to select() device handle.\n");
+ state = FATAL_ERROR;
+ }
+ }
+ else if (ready == 0)
+ {
+ // select() timed out
+ //
+ // If we go too long with an empty pipe, the device may have
+ // stopped sending data. One situation where I've observed this can
+ // happen is when the system resumes from hibernation. In this
+ // case, all calls to read() will block until we resend the feature
+ // report.
+ //
+ // I'm not sure why this happens, but my guess would be that the
+ // device stops sending new data if the data hasn't been read in a
+ // while.
+ state = DEVICE_HANDLE_OPEN;
+ //TODO: why do we ooscillate?
+ }
+ else
{
- case 0x42:
+ uint8_t data[8] = {0};
+ errno = 0;
+ int bytes_read = read(device_handle, &data, sizeof(data));
+ if (bytes_read < 0 && errno != EINTR)
{
- printf("bytes T\n");
- double t_celsius = value / 16.0 - 273.15;
- str_len = snprintf(buf, sizeof(buf), "%.2f", t_celsius);
- assert(str_len > 0);
- int f = open(temperature_file_path, open_mode, create_mode);
- if (f == -1)
+ if (errno == EIO)
{
- printf("ERROR: Failed to open output file for temperature.\n");
- goto error;
+ state = DEVICE_HANDLE_OPEN;
+ break;
}
- write(f, buf, str_len);
- close(f);
- } break;
+ else
+ {
+ printf("ERROR: Failed to read device handle\n");
+ state = FATAL_ERROR;
+ break;
+ }
+ }
+
+ uint8_t item = data[0];
+ uint8_t msb = data[1];
+ uint8_t lsb = data[2];
+ uint8_t checksum = data[3];
+ uint8_t end = data[4];
- case 0x50:
+ if (bytes_read == sizeof(data)
+ && end == 0x0d
+ && (item == 0x42 || item == 0x50)
+ && ((item + msb + lsb) & 0xFF) == checksum)
{
- printf("bytes C\n");
- str_len = snprintf(buf, sizeof(buf), "%d", value);
- assert(str_len > 0);
- int f = open(co2_file_path, open_mode, create_mode);
- if (f == -1)
+ uint16_t value = (((uint16_t)msb) << 8) | lsb;
+ char buf[1024] = {0};
+ int str_len = 0;
+ mode_t create_mode = S_IRUSR | S_IWUSR;
+ int open_mode = O_WRONLY | O_CREAT | O_TRUNC;
+
+ switch(item)
{
- printf("ERROR: Failed to open output file for CO2.\n");
- goto error;
+ case 0x42:
+ {
+ double t_celsius = value / 16.0 - 273.15;
+ str_len = snprintf(buf, sizeof(buf), "%.2f", t_celsius);
+ assert(str_len > 0);
+ int f = open(temperature_file_path, open_mode, create_mode);
+ if (f == -1)
+ {
+ printf("ERROR: Failed to open output file for temperature.\n");
+ state = FATAL_ERROR;
+ }
+ printf("write T\n");
+ write(f, buf, str_len);
+ close(f);
+ } break;
+
+ case 0x50:
+ {
+ str_len = snprintf(buf, sizeof(buf), "%d", value);
+ assert(str_len > 0);
+ int f = open(co2_file_path, open_mode, create_mode);
+ if (f == -1)
+ {
+ printf("ERROR: Failed to open output file for CO2.\n");
+ state = FATAL_ERROR;
+ }
+ printf("write CO2\n");
+ write(f, buf, str_len);
+ close(f);
+ } break;
}
- write(f, buf, str_len);
- close(f);
- } break;
+ }
}
}
+ break; case FATAL_ERROR:
+ running = false;
+ break; default:
+ UNREACHABLE;
+ break;
}
}
- goto cleanup;
-
-error:
- error_occurred = true;
-cleanup:
unlink(co2_file_path);
unlink(temperature_file_path);
close(device_handle);
- return error_occurred;
+ return state == FATAL_ERROR;
}