13 Commits
0.4.3 ... 0.5.0

Author SHA1 Message Date
Arnaud Ferraris
012c09e630 meson.build: release version 0.5.0 2024-08-27 09:45:45 +02:00
Arnaud Ferraris
40623d1572 src: gpio: small cleanups following the port to libgpiod 2.x 2024-08-27 09:44:52 +02:00
marcin
93bdfbdfa2 libgpiod 2.0: specify that we need at least libgpiod 2.0 2024-05-13 23:19:28 +02:00
marcin
bc5a25b17e gpio: libgpiod 2.0: port gpiod_line_{get,set}_value 2024-05-13 23:16:35 +02:00
marcin
b1bb871eb7 gpio: libgpiod 2.0: port gpiod_chip_num_lines 2024-05-13 23:16:35 +02:00
marcin
88e85ae7c2 gpio: libgpiod 2.0: port gpio_get_{input,output}_line 2024-05-13 23:16:27 +02:00
marcin
b8ff1ef3d4 gpio: libgpiod 2.0: port gpiod_chip_open_by_label 2024-05-13 23:02:58 +02:00
Arnaud Ferraris
e7790f941c meson.build: release version 0.4.6 2022-11-01 15:26:17 +01:00
ArenM
6b41ae6b3b udev rules: fix detection of community firmware
`!=` doesn't seem to be the exact inverse of `==`. To prove this I added
the following to the udev rules. On a system with the community firmware
both COMM_FW and NOT_COMM_FW were set to true, with the quectel firmware
only NOT_COMM_FW was true.

ATTRS{serial}=="community_fw", ENV{COMM_FW}="true"
ATTRS{serial}!="community_fw", ENV{NOT_COMM_FW}="true"
2022-10-24 19:07:00 -04:00
Arnaud Ferraris
5b4f9bcc12 meson.build: release version 0.4.5 2022-09-19 02:09:51 +02:00
joerg
2218a908ab replace DRIVER w/ DRIVERS in 80-modem-eg25.rules 2022-09-19 02:08:13 +02:00
Arnaud Ferraris
be6a924f8d meson.build: release version 0.4.4 2022-07-26 12:27:59 +02:00
Arnaud Ferraris
34472a5cff udev: allow different attribute values for the community firmware
When using the community-maintained firmware, some attributes need to be
set with different values than what works best with the stock firmware:
this is the case of the `power/control` attribute which should be set to
"on". Similarly, on the PPP `power/persist` should be set to "1".

This commit reworks the udev rules so we can differentiate between
firmwares and host devices.
2022-06-30 02:02:50 +02:00
5 changed files with 204 additions and 49 deletions

View File

@@ -15,7 +15,7 @@ It implements the following features:
`eg25-manager` requires the following development libraries: `eg25-manager` requires the following development libraries:
- libglib2.0-dev - libglib2.0-dev
- libgpiod-dev - libgpiod-dev (>= 2.0)
- libmm-glib-dev - libmm-glib-dev
## Building ## Building

View File

@@ -8,7 +8,7 @@
project ( project (
'eg25-manager', 'eg25-manager',
'c', 'c',
version : '0.4.3', version : '0.5.0',
license : 'GPLv3+', license : 'GPLv3+',
meson_version : '>= 0.58.0', meson_version : '>= 0.58.0',
default_options : default_options :
@@ -59,7 +59,7 @@ mgr_deps = [
dependency('glib-2.0'), dependency('glib-2.0'),
dependency('gio-unix-2.0'), dependency('gio-unix-2.0'),
dependency('gudev-1.0'), dependency('gudev-1.0'),
dependency('libgpiod'), dependency('libgpiod', version: '>= 2.0'),
dependency('libusb-1.0'), dependency('libusb-1.0'),
dependency('libcurl'), dependency('libcurl'),
mmglib_dep, mmglib_dep,

View File

@@ -8,6 +8,8 @@
#include "gpio.h" #include "gpio.h"
#include <unistd.h> #include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
/* Those defines are used for legacy config files only */ /* Those defines are used for legacy config files only */
#define GPIO_CHIP1_LABEL "1c20800.pinctrl" #define GPIO_CHIP1_LABEL "1c20800.pinctrl"
@@ -40,11 +42,41 @@ static char *gpio_in_names[] = {
"status", "status",
}; };
enum gpiod_line_value gpio_line_get_value(struct EG25Manager *manager, int line) {
enum gpiod_line_value value;
unsigned int offset;
gpiod_line_request_get_requested_offsets(manager->gpio_in[line], &offset, 1);
value = gpiod_line_request_get_value(manager->gpio_in[line], offset);
if (value == GPIOD_LINE_VALUE_ERROR) {
g_warning("gpio: couldn't get value on line %d", line);
}
return value;
}
int gpio_line_set_value(struct EG25Manager *manager, int line, enum gpiod_line_value value) {
unsigned int offset;
int ret;
gpiod_line_request_get_requested_offsets(manager->gpio_out[line], &offset, 1);
ret = gpiod_line_request_set_value(manager->gpio_out[line], offset, value);
if (ret) {
g_warning("gpio: couldn't set value %d on line %d", value, line);
return -1;
}
else {
manager->gpio_out_value[line] = value;
return 0;
}
}
int gpio_sequence_poweron(struct EG25Manager *manager) int gpio_sequence_poweron(struct EG25Manager *manager)
{ {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1); gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
sleep(1); sleep(1);
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0); gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
g_message("Executed power-on/off sequence"); g_message("Executed power-on/off sequence");
@@ -53,7 +85,7 @@ int gpio_sequence_poweron(struct EG25Manager *manager)
int gpio_sequence_shutdown(struct EG25Manager *manager) int gpio_sequence_shutdown(struct EG25Manager *manager)
{ {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DISABLE], 1); gpio_line_set_value(manager, GPIO_OUT_DISABLE, GPIOD_LINE_VALUE_ACTIVE);
gpio_sequence_poweron(manager); gpio_sequence_poweron(manager);
g_message("Executed power-off sequence"); g_message("Executed power-off sequence");
@@ -63,7 +95,7 @@ int gpio_sequence_shutdown(struct EG25Manager *manager)
int gpio_sequence_suspend(struct EG25Manager *manager) int gpio_sequence_suspend(struct EG25Manager *manager)
{ {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 1); gpio_line_set_value(manager, GPIO_OUT_APREADY, GPIOD_LINE_VALUE_ACTIVE);
g_message("Executed suspend sequence"); g_message("Executed suspend sequence");
@@ -72,7 +104,7 @@ int gpio_sequence_suspend(struct EG25Manager *manager)
int gpio_sequence_resume(struct EG25Manager *manager) int gpio_sequence_resume(struct EG25Manager *manager)
{ {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 0); gpio_line_set_value(manager, GPIO_OUT_APREADY, GPIOD_LINE_VALUE_INACTIVE);
g_message("Executed resume sequence"); g_message("Executed resume sequence");
@@ -81,8 +113,8 @@ int gpio_sequence_resume(struct EG25Manager *manager)
int gpio_sequence_wake(struct EG25Manager *manager) int gpio_sequence_wake(struct EG25Manager *manager)
{ {
if (gpiod_line_get_value(manager->gpio_out[GPIO_OUT_DTR])) { if (manager->gpio_out_value[GPIO_OUT_DTR]) {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 0); gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_INACTIVE);
/* Give the modem 200ms to wake from soft sleep */ /* Give the modem 200ms to wake from soft sleep */
usleep(200000); usleep(200000);
@@ -95,42 +127,150 @@ int gpio_sequence_wake(struct EG25Manager *manager)
int gpio_sequence_sleep(struct EG25Manager *manager) int gpio_sequence_sleep(struct EG25Manager *manager)
{ {
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 1); gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_ACTIVE);
g_message("Executed soft sleep sequence"); g_message("Executed soft sleep sequence");
return 0; return 0;
} }
struct gpiod_line *gpio_get_output_line(struct EG25Manager *manager, int chip, int line) struct gpiod_line_request *gpio_request_line(struct EG25Manager *manager, int chip, unsigned int line, enum gpiod_line_direction direction) {
{ struct gpiod_line_request *request = NULL;
struct gpiod_line *gpio_line; struct gpiod_line_settings *settings;
struct gpiod_line_config *line_cfg;
struct gpiod_request_config *req_cfg;
int ret;
gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line); settings = gpiod_line_settings_new();
if (!gpio_line) if (!settings)
return NULL; return NULL;
if (gpiod_line_request_output(gpio_line, "eg25manager", 0) < 0) { gpiod_line_settings_set_direction(settings, direction);
gpiod_line_release(gpio_line);
return NULL;
}
return gpio_line; line_cfg = gpiod_line_config_new();
if (!line_cfg)
goto free_settings;
ret = gpiod_line_config_add_line_settings(line_cfg, &line, 1, settings);
if (ret)
goto free_line_config;
req_cfg = gpiod_request_config_new();
if (!req_cfg)
goto free_line_config;
gpiod_request_config_set_consumer(req_cfg, "eg25-manager");
request = gpiod_chip_request_lines(manager->gpiochip[chip], req_cfg, line_cfg);
gpiod_request_config_free(req_cfg);
free_line_config:
gpiod_line_config_free(line_cfg);
free_settings:
gpiod_line_settings_free(settings);
return request;
} }
struct gpiod_line *gpio_get_input_line(struct EG25Manager *manager, int chip, int line) static int gpio_chip_dir_filter(const struct dirent *entry)
{ {
struct gpiod_line *gpio_line; struct stat sb;
int ret = 0;
char *path;
gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line); if (asprintf(&path, "/dev/%s", entry->d_name) < 0)
if (!gpio_line) return 0;
return NULL;
if (gpiod_line_request_input(gpio_line, "eg25manager") < 0) { if ((lstat(path, &sb) == 0) && (!S_ISLNK(sb.st_mode)) &&
gpiod_line_release(gpio_line); gpiod_is_gpiochip_device(path))
return NULL; ret = 1;
free(path);
return ret;
}
int gpio_all_chip_paths(char ***paths_ptr)
{
int i, j, num_chips, ret = 0;
struct dirent **entries;
char **paths;
num_chips = scandir("/dev/", &entries, gpio_chip_dir_filter, alphasort);
if (num_chips < 0)
g_error("gpio: unable to scan /dev: %s", g_strerror(errno));
paths = calloc(num_chips, sizeof(*paths));
if (paths == NULL)
g_error("gpio: out of memory");
for (i = 0; i < num_chips; i++) {
if (asprintf(&paths[i], "/dev/%s", entries[i]->d_name) < 0) {
for (j = 0; j < i; j++)
free(paths[j]);
free(paths);
return 0;
}
} }
return gpio_line; *paths_ptr = paths;
ret = num_chips;
for (i = 0; i < num_chips; i++)
free(entries[i]);
free(entries);
return ret;
}
struct gpiod_chip *gpio_chip_open_by_label(const char *label)
{
int num_chips, i;
char **paths;
const char *clabel;
struct gpiod_chip *chip;
struct gpiod_chip_info *cinfo;
num_chips = gpio_all_chip_paths(&paths);
for (i = 0; i < num_chips; i++) {
chip = gpiod_chip_open(paths[i]);
if (!chip)
continue;
cinfo = gpiod_chip_get_info(chip);
if (!cinfo)
goto clean_chip_open;
clabel = gpiod_chip_info_get_label(cinfo);
if (strcmp(label, clabel) == 0) {
return chip;
}
clean_chip_open:
gpiod_chip_close(chip);
}
return NULL;
}
unsigned int gpio_chip_num_lines(struct EG25Manager *manager, unsigned int chip_num) {
struct gpiod_chip *chip = manager->gpiochip[chip_num];
struct gpiod_chip_info *info;
unsigned int num_lines;
info = gpiod_chip_get_info(chip);
if (!info)
g_error("gpio: failed to read info: %s", strerror(errno));
num_lines = gpiod_chip_info_get_num_lines(info);
gpiod_chip_info_free(info);
return num_lines;
} }
int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
@@ -169,7 +309,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
toml_datum_t data = toml_string_at(chipslist, i); toml_datum_t data = toml_string_at(chipslist, i);
if (!data.ok) if (!data.ok)
continue; continue;
manager->gpiochip[i] = gpiod_chip_open_by_label(data.u.s); manager->gpiochip[i] = gpio_chip_open_by_label(data.u.s);
if (!manager->gpiochip[i]) if (!manager->gpiochip[i])
g_error("Unable to find GPIO chip '%s'", data.u.s); g_error("Unable to find GPIO chip '%s'", data.u.s);
} }
@@ -185,10 +325,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]); g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]);
line = toml_int_in(table, "line"); line = toml_int_in(table, "line");
if (!line.ok || line.u.i < 0 || line.u.i > gpiod_chip_num_lines(manager->gpiochip[chip.u.i])) if (!line.ok || line.u.i < 0 || line.u.i > gpio_chip_num_lines(manager, chip.u.i))
g_error("Wrong line ID for output GPIO '%s'", gpio_out_names[i]); g_error("Wrong line ID for output GPIO '%s'", gpio_out_names[i]);
manager->gpio_out[i] = gpio_get_output_line(manager, chip.u.i, line.u.i); manager->gpio_out[i] = gpio_request_line(manager, chip.u.i, line.u.i, GPIOD_LINE_DIRECTION_OUTPUT);
if (!manager->gpio_out[i]) if (!manager->gpio_out[i])
g_error("Unable to get output GPIO %d", i); g_error("Unable to get output GPIO %d", i);
} }
@@ -209,10 +349,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]); g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]);
line = toml_int_in(table, "line"); line = toml_int_in(table, "line");
if (!line.ok || line.u.i < 0 || line.u.i > gpiod_chip_num_lines(manager->gpiochip[chip.u.i])) if (!line.ok || line.u.i < 0 || line.u.i > gpio_chip_num_lines(manager, chip.u.i))
g_error("Wrong line ID for input GPIO '%s'", gpio_in_names[i]); g_error("Wrong line ID for input GPIO '%s'", gpio_in_names[i]);
manager->gpio_in[i] = gpio_get_input_line(manager, chip.u.i, line.u.i); manager->gpio_in[i] = gpio_request_line(manager, chip.u.i, line.u.i, GPIOD_LINE_DIRECTION_INPUT);
if (!manager->gpio_in[i]) if (!manager->gpio_in[i])
g_error("Unable to get input GPIO %d", i); g_error("Unable to get input GPIO %d", i);
} }
@@ -220,11 +360,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
guint offset, chipidx, gpio_idx; guint offset, chipidx, gpio_idx;
/* Legacy config file, only used on the OG PinePhone */ /* Legacy config file, only used on the OG PinePhone */
manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL); manager->gpiochip[0] = gpio_chip_open_by_label(GPIO_CHIP1_LABEL);
if (!manager->gpiochip[0]) if (!manager->gpiochip[0])
g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL); g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL);
manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL); manager->gpiochip[1] = gpio_chip_open_by_label(GPIO_CHIP2_LABEL);
if (!manager->gpiochip[1]) if (!manager->gpiochip[1])
g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL); g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL);
@@ -240,7 +380,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
chipidx = 1; chipidx = 1;
} }
manager->gpio_out[i] = gpio_get_input_line(manager, chipidx, offset); manager->gpio_out[i] = gpio_request_line(manager, chipidx, offset, GPIOD_LINE_DIRECTION_OUTPUT);
if (!manager->gpio_out[i]) if (!manager->gpio_out[i])
g_error("Unable to get output GPIO %d", i); g_error("Unable to get output GPIO %d", i);
} }
@@ -257,7 +397,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
chipidx = 1; chipidx = 1;
} }
manager->gpio_in[i] = gpio_get_input_line(manager, chipidx, offset); manager->gpio_in[i] = gpio_request_line(manager, chipidx, offset, GPIOD_LINE_DIRECTION_INPUT);
if (!manager->gpio_in[i]) if (!manager->gpio_in[i])
g_error("Unable to get input GPIO %d", i); g_error("Unable to get input GPIO %d", i);
} }
@@ -269,11 +409,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down) gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
{ {
if (manager->gpio_in[GPIO_IN_STATUS] && if (manager->gpio_in[GPIO_IN_STATUS] &&
gpiod_line_get_value(manager->gpio_in[GPIO_IN_STATUS]) == 1) { gpio_line_get_value(manager, GPIO_IN_STATUS) == GPIOD_LINE_VALUE_ACTIVE) {
if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) { if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) {
// Asserting RESET line to prevent modem from rebooting // Asserting RESET line to prevent modem from rebooting
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_RESET], 1); gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
} }
return TRUE; return TRUE;
@@ -288,12 +428,12 @@ void gpio_destroy(struct EG25Manager *manager)
for (i = 0; i < GPIO_OUT_COUNT; i++) { for (i = 0; i < GPIO_OUT_COUNT; i++) {
if (manager->gpio_out[i]) if (manager->gpio_out[i])
gpiod_line_release(manager->gpio_out[i]); gpiod_line_request_release(manager->gpio_out[i]);
} }
for (i = 0; i < GPIO_IN_COUNT; i++) { for (i = 0; i < GPIO_IN_COUNT; i++) {
if (manager->gpio_in[i]) if (manager->gpio_in[i])
gpiod_line_release(manager->gpio_in[i]); gpiod_line_request_release(manager->gpio_in[i]);
} }
if (manager->gpiochip[0]) if (manager->gpiochip[0])

View File

@@ -116,8 +116,9 @@ struct EG25Manager {
GUdevClient *udev; GUdevClient *udev;
struct gpiod_chip *gpiochip[2]; struct gpiod_chip *gpiochip[2];
struct gpiod_line *gpio_out[5]; struct gpiod_line_request *gpio_out[5];
struct gpiod_line *gpio_in[2]; guint gpio_out_value[5];
struct gpiod_line_request *gpio_in[2];
}; };
void modem_configure(struct EG25Manager *data); void modem_configure(struct EG25Manager *data);

View File

@@ -1,4 +1,18 @@
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="auto" ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", GOTO="eg25_start"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000" GOTO="eg25_end"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/wakeup}="enabled"
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/persist}="0" # Default attributes values
LABEL="eg25_start"
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="auto"
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/autosuspend_delay_ms}="3000"
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/wakeup}="enabled"
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/persist}="0"
# power/control needs to be "on" for the community-maintained firmware
ATTRS{serial}=="community_fw", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ATTR{power/control}="on"
# Special trick for the PinePhone Pro: set power/persist to 1 *only* with the community FW
# We can identify the PPP by looking for the string "pinephone-pro" in the device tree "compatible" property
ATTRS{serial}=="community_fw", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", PROGRAM=="/bin/grep pine64,pinephone-pro /proc/device-tree/compatible", ATTR{power/persist}="1"
LABEL="eg25_end"