From a06360f4c84730865f2e08a39d907b287a24755b Mon Sep 17 00:00:00 2001 From: ArenM Date: Sat, 6 Nov 2021 17:19:20 -0400 Subject: [PATCH 1/5] at: break before overflow when receiving messages Previously this code checked if the buffer was full after writing to it, which meant that the buffer could overflow. This checks for an overflow before copying into the buffer and only copies the data that will fit. --- src/at.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/at.c b/src/at.c index ee99ff4..56dbe76 100644 --- a/src/at.c +++ b/src/at.c @@ -245,7 +245,14 @@ static gboolean modem_response(gint fd, */ do { ret = read(fd, tmp, sizeof(tmp)); + if (ret > 0) { + /* If we're going to overflow truncate the data we read to fit */ + if (pos + ret >= sizeof(response)) { + g_critical("AT response buffer full, truncating"); + ret = sizeof(response) - (pos + 1); + } + memcpy(&response[pos], tmp, ret); pos += ret; usleep(10000); From f8b3e28434e3b2c5ca111a57d5d2af07f7f1d381 Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris Date: Wed, 24 Nov 2021 01:02:11 +0100 Subject: [PATCH 2/5] config: add config_get_table helper function --- src/config.c | 14 ++++++++++++++ src/config.h | 1 + 2 files changed, 15 insertions(+) diff --git a/src/config.c b/src/config.c index d4ce151..2c5aafa 100644 --- a/src/config.c +++ b/src/config.c @@ -81,3 +81,17 @@ gboolean config_get_array(toml_table_t **config, const gchar *key, toml_array_t return !!array; } + +gboolean config_get_table(toml_table_t **config, const gchar *key, toml_table_t **result) +{ + toml_table_t *table = NULL; + + if (config[EG25_CONFIG_USER]) + table = toml_table_in(config[EG25_CONFIG_USER], key); + if (!table) + table = toml_table_in(config[EG25_CONFIG_SYS], key); + if (table && result) + *result = table; + + return !!table; +} diff --git a/src/config.h b/src/config.h index d1a214f..5f2d1e2 100644 --- a/src/config.h +++ b/src/config.h @@ -24,3 +24,4 @@ gboolean config_get_int(toml_table_t **config, const gchar *key, gint *result); gboolean config_get_uint(toml_table_t **config, const gchar *key, guint *result); gboolean config_get_string(toml_table_t **config, const gchar *key, gchar **result); gboolean config_get_array(toml_table_t **config, const gchar *key, toml_array_t **result); +gboolean config_get_table(toml_table_t **config, const gchar *key, toml_table_t **result); From abf60b793a70f202f8fbefea59d82577ca575078 Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris Date: Wed, 24 Nov 2021 01:17:57 +0100 Subject: [PATCH 3/5] gpio: make more generic Instead of assuming we're running on the PinePhone (and therefore hardcoding gpiochip labels and line numbers), make all of those configurable. Legacy config files will still be parsed as long as they lack the `chips` key. Existing config files are also updated to match the new config format. --- data/pine64,pinephone-1.0.toml | 11 +- data/pine64,pinephone-1.1.toml | 11 +- data/pine64,pinephone-1.2.toml | 13 +-- src/gpio.c | 181 ++++++++++++++++++++++++--------- 4 files changed, 152 insertions(+), 64 deletions(-) diff --git a/data/pine64,pinephone-1.0.toml b/data/pine64,pinephone-1.0.toml index 8b91a88..10dbc1e 100644 --- a/data/pine64,pinephone-1.0.toml +++ b/data/pine64,pinephone-1.0.toml @@ -13,11 +13,12 @@ poweron_delay = 100000 #recovery_timeout = 9 [gpio] -dtr = 358 -pwrkey = 35 -reset = 68 -apready = 231 -disable = 232 +chips = [ "1c20800.pinctrl", "1f02c00.pinctrl" ] +dtr = { chip = 1, line = 6 } +pwrkey = { chip = 0, line = 35 } +reset = { chip = 0, line = 68 } +apready = { chip = 0, line = 231 } +disable = { chip = 0, line = 232 } [at] uart = "/dev/ttyS2" diff --git a/data/pine64,pinephone-1.1.toml b/data/pine64,pinephone-1.1.toml index fd09462..9242a91 100644 --- a/data/pine64,pinephone-1.1.toml +++ b/data/pine64,pinephone-1.1.toml @@ -13,11 +13,12 @@ poweron_delay = 100000 #recovery_timeout = 9 [gpio] -dtr = 358 -pwrkey = 35 -reset = 68 -apready = 231 -disable = 232 +chips = [ "1c20800.pinctrl", "1f02c00.pinctrl" ] +dtr = { chip = 1, line = 6 } +pwrkey = { chip = 0, line = 35 } +reset = { chip = 0, line = 68 } +apready = { chip = 0, line = 231 } +disable = { chip = 0, line = 232 } [at] uart = "/dev/ttyS2" diff --git a/data/pine64,pinephone-1.2.toml b/data/pine64,pinephone-1.2.toml index 4244bee..3754274 100644 --- a/data/pine64,pinephone-1.2.toml +++ b/data/pine64,pinephone-1.2.toml @@ -9,12 +9,13 @@ poweron_delay = 100000 #recovery_timeout = 9 [gpio] -dtr = 34 -pwrkey = 35 -reset = 68 -apready = 231 -disable = 232 -status = 233 +chips = [ "1c20800.pinctrl" ] +dtr = { chip = 0, line = 34 } +pwrkey = { chip = 0, line = 35 } +reset = { chip = 0, line = 68 } +apready = { chip = 0, line = 231 } +disable = { chip = 0, line = 232 } +status = { chip = 0, line = 233 } [at] uart = "/dev/ttyS2" diff --git a/src/gpio.c b/src/gpio.c index c7cfc53..f9e707c 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -9,9 +9,9 @@ #include +/* Those defines are used for legacy config files only */ #define GPIO_CHIP1_LABEL "1c20800.pinctrl" #define GPIO_CHIP2_LABEL "1f02c00.pinctrl" - #define MAX_GPIOCHIP_LINES 352 enum { @@ -101,10 +101,41 @@ int gpio_sequence_sleep(struct EG25Manager *manager) return 0; } +struct gpiod_line *gpio_get_output_line(struct EG25Manager *manager, int chip, int line) +{ + struct gpiod_line *gpio_line; + + gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line); + if (!gpio_line) + return NULL; + + if (gpiod_line_request_output(gpio_line, "eg25manager", 0) < 0) { + gpiod_line_release(gpio_line); + return NULL; + } + + return gpio_line; +} + +struct gpiod_line *gpio_get_input_line(struct EG25Manager *manager, int chip, int line) +{ + struct gpiod_line *gpio_line; + + gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line); + if (!gpio_line) + return NULL; + + if (gpiod_line_request_input(gpio_line, "eg25manager") < 0) { + gpiod_line_release(gpio_line); + return NULL; + } + + return gpio_line; +} + int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) { - int i, ret; - guint offset, chipidx, gpio_idx; + int i; toml_table_t *gpio_config[EG25_CONFIG_COUNT]; for (i = 0; i < EG25_CONFIG_COUNT; i++) @@ -113,65 +144,119 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) if (!gpio_config[EG25_CONFIG_SYS]) g_error("Default config file lacks the 'gpio' section!"); - manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL); - if (!manager->gpiochip[0]) { - g_critical("Unable to open GPIO chip " GPIO_CHIP1_LABEL); - return 1; - } + /* + * The system config could have the `chips` key, but the user one + * could still use the old format! In order to avoid problems, we + * should use the new format only if: + * - there's no user config file + or + * - the user config file contains the `chips` key + * Otherwise we might start parsing the system config with the new + * format, but error out if user config overrides gpios using the + * old format + */ + if (!gpio_config[EG25_CONFIG_USER] || toml_array_in(gpio_config[EG25_CONFIG_USER], "chips")) + { + int numchips; + toml_array_t *chipslist = NULL; - manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL); - if (!manager->gpiochip[1]) { - g_critical("Unable to open GPIO chip " GPIO_CHIP2_LABEL); - return 1; - } + config_get_array(gpio_config, "chips", &chipslist); + numchips = toml_array_nelem(chipslist); + if (numchips > 2) + g_error("Requesting too many GPIO chips!"); - for (i = 0; i < GPIO_OUT_COUNT; i++) { - if (!config_get_uint(gpio_config, gpio_out_names[i], &gpio_idx)) - g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]); - - if (gpio_idx < MAX_GPIOCHIP_LINES) { - offset = gpio_idx; - chipidx = 0; - } else { - offset = gpio_idx - MAX_GPIOCHIP_LINES; - chipidx = 1; + for (i = 0; i < numchips; i++) { + toml_datum_t data = toml_string_at(chipslist, i); + if (!data.ok) + continue; + manager->gpiochip[i] = gpiod_chip_open_by_label(data.u.s); } - manager->gpio_out[i] = gpiod_chip_get_line(manager->gpiochip[chipidx], offset); - if (!manager->gpio_out[i]) { - g_error("Unable to get output GPIO %d", i); + for (i = 0; i < GPIO_OUT_COUNT; i++) { + toml_table_t *table; + toml_datum_t chip, line; + if (!config_get_table(gpio_config, gpio_out_names[i], &table)) + g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]); + + chip = toml_int_in(table, "chip"); + if (!chip.ok || chip.u.i < 0 || chip.u.i > 2) + g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]); + + 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])) + g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]); + + manager->gpio_out[i] = gpio_get_output_line(manager, chip.u.i, line.u.i); + if (!manager->gpio_out[i]) + g_error("Unable to get output GPIO %d", i); + } + + for (i = 0; i < GPIO_IN_COUNT; i++) { + toml_table_t *table; + toml_datum_t chip, line; + if (!config_get_table(gpio_config, gpio_in_names[i], &table)) + g_error("Unable to get config for input GPIO '%s'", gpio_in_names[i]); + + chip = toml_int_in(table, "chip"); + if (!chip.ok || chip.u.i < 0 || chip.u.i > 2) + g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]); + + 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])) + g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]); + + manager->gpio_in[i] = gpio_get_input_line(manager, chip.u.i, line.u.i); + if (!manager->gpio_in[i]) + g_error("Unable to get input GPIO %d", i); + } + } else { + guint offset, chipidx, gpio_idx; + + /* Legacy config file, only used on the OG PinePhone */ + manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL); + if (!manager->gpiochip[0]) { + g_critical("Unable to open GPIO chip " GPIO_CHIP1_LABEL); return 1; } - ret = gpiod_line_request_output(manager->gpio_out[i], "eg25manager", 0); - if (ret < 0) { - g_error("Unable to request output GPIO %d", i); + manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL); + if (!manager->gpiochip[1]) { + g_critical("Unable to open GPIO chip " GPIO_CHIP2_LABEL); return 1; } - } - for (i = 0; i < GPIO_IN_COUNT; i++) { - if (!config_get_uint(gpio_config, gpio_in_names[i], &gpio_idx)) - continue; + for (i = 0; i < GPIO_OUT_COUNT; i++) { + if (!config_get_uint(gpio_config, gpio_out_names[i], &gpio_idx)) + g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]); - if (gpio_idx < MAX_GPIOCHIP_LINES) { - offset = gpio_idx; - chipidx = 0; - } else { - offset = gpio_idx - MAX_GPIOCHIP_LINES; - chipidx = 1; + if (gpio_idx < MAX_GPIOCHIP_LINES) { + offset = gpio_idx; + chipidx = 0; + } else { + offset = gpio_idx - MAX_GPIOCHIP_LINES; + chipidx = 1; + } + + manager->gpio_out[i] = gpio_get_input_line(manager, chipidx, offset); + if (!manager->gpio_out[i]) + g_error("Unable to get output GPIO %d", i); } - manager->gpio_in[i] = gpiod_chip_get_line(manager->gpiochip[chipidx], offset); - if (!manager->gpio_in[i]) { - g_warning("Unable to get input GPIO %d", i); - continue; - } + for (i = 0; i < GPIO_IN_COUNT; i++) { + if (!config_get_uint(gpio_config, gpio_in_names[i], &gpio_idx)) + continue; - ret = gpiod_line_request_input(manager->gpio_in[i], "eg25manager"); - if (ret < 0) { - g_warning("Unable to request input GPIO %d", i); - manager->gpio_in[i] = NULL; + if (gpio_idx < MAX_GPIOCHIP_LINES) { + offset = gpio_idx; + chipidx = 0; + } else { + offset = gpio_idx - MAX_GPIOCHIP_LINES; + chipidx = 1; + } + + manager->gpio_in[i] = gpio_get_input_line(manager, chipidx, offset); + if (!manager->gpio_in[i]) + g_error("Unable to get input GPIO %d", i); } } From b21c4b0fa4d857e513363f5bb561c54846011a43 Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris Date: Wed, 24 Nov 2021 01:18:21 +0100 Subject: [PATCH 4/5] data: add PinePhone Pro config The PinePhone Pro uses the same EG25 modem as the OG PinePhone, but with a different SoC. It also uses an ALC5616 audio codec directly hooked up to the modem (which is I2S master in this case). The config is therefore almost identical to the PinePhone rev1.2 except for the gpios, UART port used and `AT+QDAI` config (to account for the different audio setup). --- data/meson.build | 1 + data/pine64,pinephone-pro.toml | 97 ++++++++++++++++++++++++++++++++++ src/gpio.c | 18 +++---- 3 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 data/pine64,pinephone-pro.toml diff --git a/data/meson.build b/data/meson.build index 56bc6e3..f1fc8f0 100644 --- a/data/meson.build +++ b/data/meson.build @@ -8,6 +8,7 @@ conf_files = [ 'pine64,pinephone-1.0.toml', 'pine64,pinephone-1.1.toml', 'pine64,pinephone-1.2.toml', +'pine64,pinephone-pro.toml', ] install_data(conf_files) diff --git a/data/pine64,pinephone-pro.toml b/data/pine64,pinephone-pro.toml new file mode 100644 index 0000000..492de96 --- /dev/null +++ b/data/pine64,pinephone-pro.toml @@ -0,0 +1,97 @@ +[manager] +# Delay between setting GPIO and PWRKEY sequence, set in microseconds +poweron_delay = 100000 + +# Uncomment the following if you need to change the modem detection timeout on +# resume and/or the time during which suspend is blocked after modem boot +#[suspend] +#boot_timeout = 120 +#recovery_timeout = 9 + +[gpio] +chips = [ "gpio0", "gpio3" ] +dtr = { chip = 0, line = 3 } +pwrkey = { chip = 0, line = 13 } +reset = { chip = 1, line = 8 } +apready = { chip = 0, line = 12 } +disable = { chip = 0, line = 8 } +status = { chip = 1, line = 6 } + +[at] +uart = "/dev/ttyS3" +configure = [ +# Each command has 4 possible elements: +# * `cmd` : the AT command itself, which will be translated to "AT+`cmd`" +# * `subcmd`: the subcommand in case a single AT command can be used +# to change multiple parameters, such as QCFG (optional) +# * `value` : the commands, argument, usually used to set the value of +# a specific parameter (optional) +# * `expect`: the expected return value; the command is first executed +# without any value in order to query the current state. This +# state is then compared to the `expect` string; if they don't +# match, the command is then executed with value `expect` in +# order to set the parameter to the configured value (optional) +# A command can have `expect` OR `value` configured, but it shouldn't have both +# Print software version + { cmd = "QGMR" }, +# Configure audio + { cmd = "QDAI", expect = "3,0,0,4,0,0,1,1" }, +# RI signaling using physical RI pin + { cmd = "QCFG", subcmd = "risignaltype", expect = "\"physical\"" }, +# Enable VoLTE support + { cmd = "QCFG", subcmd = "ims", expect = "1" }, +# Enable APREADY for PP 1.2 + { cmd = "QCFG", subcmd = "apready", expect = "1,0,500" }, +# URC configuration for PP 1.2 (APREADY pin connected): +# * RING URC: normal pulse length +# * Incoming SMS URC: default pulse length +# * Other URC: default length +# * Report URCs on all ports (serial and USB) for FOSS firmware +# * Reporting of URCs without any delay +# * Configure URC pin to UART Ring Indicator + { cmd = "QCFG", subcmd = "urc/ri/ring", expect = "\"pulse\",120,1000,5000,\"off\",1" }, + { cmd = "QCFG", subcmd = "urc/ri/smsincoming", expect = "\"pulse\",120,1" }, + { cmd = "QCFG", subcmd = "urc/ri/other", expect = "\"off\",1,1" }, + { cmd = "QCFG", subcmd = "urc/delay", expect = "0" }, + { cmd = "QCFG", subcmd = "urc/cache", expect = "0" }, + { cmd = "QCFG", subcmd = "urc/ri/pin", expect = "uart_ri" }, + { cmd = "QURCCFG", subcmd = "urcport", expect = "\"all\"" }, +# Allow sleeping for power saving + { cmd = "QSCLK", value = "1" }, +# GNSS configuration: +# * Enable A-GPS data upload support (XTRA) +# * Disable On-Demand-Positioning (ODP) mode +# to avoid running the GNSS system in the background, even when not enabled. +# * Enable Dynamic Power Optimizations (DPO) mode to turn off GNSS RF radios +# when they are not in use. +# * Only enable GPS and GLONASS, disable other GNSS systems. +# A-GPS data upload doesn't work for Galileo anyway. +# * Avoid turning on GNSS support automatically when the modem boots. + { cmd = "QGPSXTRA", expect = "1" }, + { cmd = "QGPSCFG", subcmd = "gnssconfig", expect = "4" }, + { cmd = "QGPSCFG", subcmd = "odpcontrol", expect = "0" }, + { cmd = "QGPSCFG", subcmd = "dpoenable", expect = "1" }, + { cmd = "QGPSCFG", subcmd = "gpsnmeatype", expect = "31" }, + { cmd = "QGPSCFG", subcmd = "glonassnmeatype", expect = "7" }, + { cmd = "QGPSCFG", subcmd = "galileonmeatype", expect = "0" }, + { cmd = "QGPSCFG", subcmd = "beidounmeatype", expect = "0" }, + { cmd = "QGPSCFG", subcmd = "autogps", expect = "0" }, +# Disable fast poweroff for stability + { cmd = "QCFG", subcmd = "fast/poweroff", expect = "0" }, +# Configure sleep and wake up pin levels to active low + { cmd = "QCFG", subcmd = "sleepind/level", expect = "0" }, + { cmd = "QCFG", subcmd = "wakeupin/level", expect = "0,0" }, +# Do not enter RAMDUMP mode, auto-reset instead + { cmd = "QCFG", subcmd = "ApRstLevel", expect = "1" }, + { cmd = "QCFG", subcmd = "ModemRstLevel", expect = "1" }, +] +suspend = [ +] +resume = [ +] +reset = [ { cmd = "CFUN", value = "1,1" } ] + +[gnss] +enabled = true +url = "https://xtrapath4.izatcloud.net" +file = "xtra2.bin" diff --git a/src/gpio.c b/src/gpio.c index f9e707c..49bee0d 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -170,6 +170,8 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) if (!data.ok) continue; manager->gpiochip[i] = gpiod_chip_open_by_label(data.u.s); + if (!manager->gpiochip[i]) + g_error("Unable to find GPIO chip '%s'", data.u.s); } for (i = 0; i < GPIO_OUT_COUNT; i++) { @@ -184,7 +186,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) 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])) - g_error("Wrong chip 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); if (!manager->gpio_out[i]) @@ -203,7 +205,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) 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])) - g_error("Wrong chip 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); if (!manager->gpio_in[i]) @@ -214,16 +216,12 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) /* Legacy config file, only used on the OG PinePhone */ manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL); - if (!manager->gpiochip[0]) { - g_critical("Unable to open GPIO chip " GPIO_CHIP1_LABEL); - return 1; - } + if (!manager->gpiochip[0]) + g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL); manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL); - if (!manager->gpiochip[1]) { - g_critical("Unable to open GPIO chip " GPIO_CHIP2_LABEL); - return 1; - } + if (!manager->gpiochip[1]) + g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL); for (i = 0; i < GPIO_OUT_COUNT; i++) { if (!config_get_uint(gpio_config, gpio_out_names[i], &gpio_idx)) From a91bc71e23e5a8d5e17b32bb5fa8f3691251dfeb Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris Date: Wed, 8 Dec 2021 18:21:48 +0100 Subject: [PATCH 5/5] meson.build: release version 0.4.2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d8d2202..535927a 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project ( 'eg25-manager', 'c', - version : '0.4.1', + version : '0.4.2', license : 'GPLv3+', meson_version : '>= 0.50.0', default_options :