7 Commits
0.5.0 ... 0.5.1

Author SHA1 Message Date
Arnaud Ferraris
b000a75238 meson.build: release version 0.5.1 2024-10-28 09:37:49 +01:00
Aren Moynihan
541aa00198 gpio: disable airplane mode when starting the modem
I'm not sure why this was being done when disabling the modem, move it
to the powerup sequence for more consistency.
2024-10-21 16:54:58 -04:00
Aren Moynihan
b53702ad0b manager: modem_reset: fix file descriptor leek
fd wasn't being closed if the write failed. Theoretically if a system
with an unstable modem ran for long enough, this could bring down
eg25-manager.
2024-10-21 16:54:58 -04:00
Aren Moynihan
1482899888 manager: reset using gpio pins if the mode is unresponsive
If the modem doesn't respond to the at reboot sequence, this will
attempt reboot the modem using both the power pin and then the reset pin
in case that doesn't work.
2024-10-21 16:54:58 -04:00
Aren Moynihan
c4b3b75047 manager: always set the reset line when exiting
This splits the logic from gpio_check_poweroff into two separate
functions, allowing the caller more control over when the reset line is
asserted to force the modem to turn off.

Previously if the modem didn't respond to the power key sequence in
quit_app, gpio_check_poweroff wouldn't set the reset pin, and
eg25-manager could leave the modem running after exiting.
2024-10-04 17:11:26 -04:00
Aren Moynihan
3c5d455f20 gpio: toggle the RESET_N pin before booting the modem to ensure it is actually off 2024-10-04 17:08:45 -04:00
Arnaud Ferraris
1acd15ca2f src: gnss: fix build on 32-bit architectures
It seems `off_t` is now 64-bits wide on 32-bit architectures, so using
`glong` causes a build failure in this case. Switch to using the correct
type from the `sendfile()` prototype to fix this.
2024-08-27 11:01:08 +02:00
5 changed files with 89 additions and 22 deletions

View File

@@ -8,7 +8,7 @@
project ( project (
'eg25-manager', 'eg25-manager',
'c', 'c',
version : '0.5.0', version : '0.5.1',
license : 'GPLv3+', license : 'GPLv3+',
meson_version : '>= 0.58.0', meson_version : '>= 0.58.0',
default_options : default_options :

View File

@@ -327,7 +327,7 @@ static void init_assistance_data_upload(struct EG25Manager *manager)
static void upload_assistance_data(struct EG25Manager *manager) static void upload_assistance_data(struct EG25Manager *manager)
{ {
gint error; gint error;
glong written_total = 0; off_t written_total = 0;
gint ret; gint ret;
struct stat sb; struct stat sb;

View File

@@ -74,19 +74,49 @@ int gpio_line_set_value(struct EG25Manager *manager, int line, enum gpiod_line_v
int gpio_sequence_poweron(struct EG25Manager *manager) int gpio_sequence_poweron(struct EG25Manager *manager)
{ {
/* Disable airplane mode in case it was enabled by other software. The
* W_DISABLE pin is active-low, so we set it to high here. */
gpio_line_set_value(manager, GPIO_OUT_DISABLE, GPIOD_LINE_VALUE_ACTIVE);
/*
* Force the modem to poweroff using the RESET_N pin before attempting to
* boot in case the it got into a bad state.
*
* If the modem was on, this will cause it to start booting, so press the
* power button while in reset to avoid a (probably only theoretical) race
* condition where it starts booting after reset, and then powers off from
* the power key.
*/
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE); gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
/*
* The datasheet says to pull the pin low for between 150 and 460 ms. usleep
* should always sleep for at least the specified amount of time, so use
* 200ms because it's closer to the bottom of that range.
*/
usleep(200000);
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_INACTIVE);
/*
* The modem has finished it's reset, now we wait to allow it a chance to
* react to the power key
*/
sleep(1); sleep(1);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE); gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
g_message("Executed power-on/off sequence"); g_message("Executed power-on sequence");
return 0; return 0;
} }
int gpio_sequence_shutdown(struct EG25Manager *manager) int gpio_sequence_shutdown(struct EG25Manager *manager)
{ {
gpio_line_set_value(manager, GPIO_OUT_DISABLE, GPIOD_LINE_VALUE_ACTIVE); gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
gpio_sequence_poweron(manager); sleep(1);
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
g_message("Executed power-off sequence"); g_message("Executed power-off sequence");
@@ -406,16 +436,18 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
return 0; return 0;
} }
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down) void gpio_force_off(struct EG25Manager *manager)
{
if (manager->gpio_out[GPIO_OUT_RESET]) {
g_message("Setting the reset pin to ensure the modem stays off");
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
}
}
gboolean gpio_check_poweroff(struct EG25Manager *manager)
{ {
if (manager->gpio_in[GPIO_IN_STATUS] && if (manager->gpio_in[GPIO_IN_STATUS] &&
gpio_line_get_value(manager, GPIO_IN_STATUS) == GPIOD_LINE_VALUE_ACTIVE) { gpio_line_get_value(manager, GPIO_IN_STATUS) == GPIOD_LINE_VALUE_ACTIVE) {
if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) {
// Asserting RESET line to prevent modem from rebooting
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
}
return TRUE; return TRUE;
} }

View File

@@ -18,4 +18,5 @@ int gpio_sequence_resume(struct EG25Manager *state);
int gpio_sequence_wake(struct EG25Manager *state); int gpio_sequence_wake(struct EG25Manager *state);
int gpio_sequence_sleep(struct EG25Manager *state); int gpio_sequence_sleep(struct EG25Manager *state);
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down); void gpio_force_off(struct EG25Manager *manager);
gboolean gpio_check_poweroff(struct EG25Manager *manager);

View File

@@ -62,13 +62,17 @@ static gboolean quit_app(struct EG25Manager *manager)
gpio_sequence_shutdown(manager); gpio_sequence_shutdown(manager);
manager->modem_state = EG25_STATE_FINISHING; manager->modem_state = EG25_STATE_FINISHING;
for (i = 0; i < 30; i++) { for (i = 0; i < 30; i++) {
if (gpio_check_poweroff(manager, TRUE)) if (gpio_check_poweroff(manager)) {
g_message("Modem successfully powered down");
break; break;
}
sleep(1); sleep(1);
} }
} }
g_message("Modem down, quitting...");
gpio_force_off(manager);
g_message("Modem down, quitting...");
g_main_loop_quit(manager->loop); g_main_loop_quit(manager->loop);
return FALSE; return FALSE;
@@ -98,7 +102,7 @@ static gboolean modem_start(struct EG25Manager *manager)
libusb_free_device_list(devices, 1); libusb_free_device_list(devices, 1);
libusb_exit(ctx); libusb_exit(ctx);
} else if (!gpio_check_poweroff(manager, FALSE)) { } else if (!gpio_check_poweroff(manager)) {
g_message("STATUS is low, modem already powered"); g_message("STATUS is low, modem already powered");
should_boot = FALSE; should_boot = FALSE;
} }
@@ -141,7 +145,35 @@ void modem_configure(struct EG25Manager *manager)
at_sequence_configure(manager); at_sequence_configure(manager);
} }
static gboolean modem_reset_done(struct EG25Manager* manager) static gboolean modem_gpio_reset_done(struct EG25Manager *manager)
{
gpio_sequence_poweron(manager);
manager->modem_state = EG25_STATE_POWERED;
manager->complete_reset_timer = 0;
return G_SOURCE_REMOVE;
}
static gboolean modem_at_reset_done(struct EG25Manager* manager)
{
/*
* If the modem was successfully rebooted, then we should have received
* "RDY" by now which will transition the state to started.
*/
if (manager->modem_state != EG25_STATE_RESETTING) {
manager->complete_reset_timer = 0;
return G_SOURCE_REMOVE;
}
g_message("AT reset failed, falling back to GPIO reset");
gpio_sequence_shutdown(manager);
manager->complete_reset_timer = g_timeout_add_seconds(30, G_SOURCE_FUNC(modem_gpio_reset_done), manager);
return G_SOURCE_REMOVE;
}
static gboolean modem_rebind_done(struct EG25Manager* manager)
{ {
manager->modem_state = EG25_STATE_RESUMING; manager->modem_state = EG25_STATE_RESUMING;
manager->complete_reset_timer = 0; manager->complete_reset_timer = 0;
@@ -194,24 +226,26 @@ gboolean modem_reset(struct EG25Manager *manager)
g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind"); g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind");
goto error; goto error;
} }
ret = write(fd, manager->modem_usb_id, len); ret = write(fd, manager->modem_usb_id, len);
close(fd);
if (ret < len) { if (ret < len) {
g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len); g_warning("Couldn't unbind modem: wrote %d/%d bytes", ret, len);
goto error; goto error;
} }
close(fd);
fd = open("/sys/bus/usb/drivers/usb/bind", O_WRONLY); fd = open("/sys/bus/usb/drivers/usb/bind", O_WRONLY);
if (fd < 0) { if (fd < 0) {
g_warning("Unable to open /sys/bus/usb/drivers/usb/unbind"); g_warning("Unable to open /sys/bus/usb/drivers/usb/bind");
goto error; goto error;
} }
ret = write(fd, manager->modem_usb_id, len); ret = write(fd, manager->modem_usb_id, len);
close(fd);
if (ret < len) { if (ret < len) {
g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len); g_warning("Couldn't bind modem: wrote %d/%d bytes", ret, len);
goto error; goto error;
} }
close(fd);
g_message("Successfully reset modem's USB connection"); g_message("Successfully reset modem's USB connection");
@@ -219,7 +253,7 @@ gboolean modem_reset(struct EG25Manager *manager)
* 3s is long enough to make sure the modem has been bound back and * 3s is long enough to make sure the modem has been bound back and
* short enough to ensure it hasn't been acquired by ModemManager * short enough to ensure it hasn't been acquired by ModemManager
*/ */
manager->complete_reset_timer = g_timeout_add_seconds(3, G_SOURCE_FUNC(modem_reset_done), manager); manager->complete_reset_timer = g_timeout_add_seconds(3, G_SOURCE_FUNC(modem_rebind_done), manager);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
@@ -237,7 +271,7 @@ error:
at_sequence_reset(manager); at_sequence_reset(manager);
// Setup timer for making sure we don't queue other reset commands // Setup timer for making sure we don't queue other reset commands
manager->complete_reset_timer = g_timeout_add_seconds(30, G_SOURCE_FUNC(modem_reset_done), manager); manager->complete_reset_timer = g_timeout_add_seconds(30, G_SOURCE_FUNC(modem_at_reset_done), manager);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }