6 Commits
0.4.1 ... 0.4.2

Author SHA1 Message Date
Arnaud Ferraris
a91bc71e23 meson.build: release version 0.4.2 2021-12-08 18:21:48 +01:00
Arnaud Ferraris
b21c4b0fa4 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).
2021-11-27 13:12:17 +01:00
Arnaud Ferraris
abf60b793a 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.
2021-11-24 01:34:14 +01:00
Arnaud Ferraris
f8b3e28434 config: add config_get_table helper function 2021-11-24 01:20:54 +01:00
Arnaud Ferraris
d9725981bb Merge branch 'at-overflow' into 'master'
at: break before overflow when receiving messages

See merge request mobian1/devices/eg25-manager!40
2021-11-10 10:50:58 +00:00
ArenM
a06360f4c8 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.
2021-11-06 19:59:22 -04:00
10 changed files with 273 additions and 67 deletions

View File

@@ -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)

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 :

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -9,9 +9,9 @@
#include <unistd.h>
/* 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,117 @@ 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);
if (!manager->gpiochip[i])
g_error("Unable to find GPIO chip '%s'", 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);
return 1;
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 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])
g_error("Unable to get output GPIO %d", i);
}
ret = gpiod_line_request_output(manager->gpio_out[i], "eg25manager", 0);
if (ret < 0) {
g_error("Unable to request output GPIO %d", i);
return 1;
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 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])
g_error("Unable to get input GPIO %d", i);
}
}
} else {
guint offset, chipidx, gpio_idx;
for (i = 0; i < GPIO_IN_COUNT; i++) {
if (!config_get_uint(gpio_config, gpio_in_names[i], &gpio_idx))
continue;
/* 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_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL);
if (gpio_idx < MAX_GPIOCHIP_LINES) {
offset = gpio_idx;
chipidx = 0;
} else {
offset = gpio_idx - MAX_GPIOCHIP_LINES;
chipidx = 1;
manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL);
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))
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;
}
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);
}
}