# Modem on PinePhone

Contribute

Quectel EG25-G modem in PinePhone handles WWAN, GPS and celular services. It is based on `Qualcomm MDM 9607` chipset, has 256 MiB DRAM, 256 MiB NAND, and a single Cortex-A7 CPU clocked up to 1.3GHz.

The modem's firmware is split into two parts:

- Linux kernel + userspace runs on an ARM CPU
- Modem's firmware runs on Hexagon ADSP

Most AT commands are run on a separeate DSP processor cores (Hexagon QDSP6 V5), with some being forwarded to a Linux userspace program called `atfwd_daemon` running on the ARM CPU.

Reverse engineering the exisintg software for ARM CPU inside the modem is quite easy using ghidra. ADB unlocker was made this way.

## Modem power driver

My kernel is a bit special, since it contains a special modem power manager driver (released 2020–08–03 along with Linux 5.8).

My driver implements several features:

- it ensures modem is configured properly for audio and sleep
- it implements airplane mode via rfkill interface
- it implements modem suspend/resume
- it hides PinePhone variant differences
- it implements URC caching on all pinephone variants regardless of AP_READY availability
- it makes the modem sleep most of the time
- it handles powerup/powerdown errors properly
- it checks for killswitch and reports it to userspace/dmesg
- it doesn't waste time during powerdown and waits for actual modem powerdown (it doesn't just sleep for a fixed time)
- it handles SoC wakeup on RI
- it shuts down the modem properly during kernel powerdown/reboot to avoid data loss inside the modem's NAND flash
- it reports all errors to dmesg for easy troubleshooting/failure detection

Enabling and disabling the power to the modem is as simple as:

    # request powerup
    echo 1 > /sys/class/modem-power/modem-power/device/powered
    # request powerdown
    echo 0 > /sys/class/modem-power/modem-power/device/powered
    # read power status (changes only after power state transition is complete)
    cat /sys/class/modem-power/modem-power/device/powered

So there's no need to mess with gpios via sysfs.

## Connecting to the modem

You can connect to the modem once it's powered up via:

    screen /dev/ttyUSB2 115200

Disconnect by `CTRL+a k`.

## Setting up the modem for voice calling

Once per a lifetime you have to run `AT+QDAI=1,0,0,2,0,1,1,1` and reboot the modem. That will configure audio on the modem side correctly and store the configuration persistently inside the modem. This is not necessary if you use my modem driver.

To setup audio for call use my call audio setup program.

You need to run the program after the call starts twice. Once with your desired audio setup without `-2` option and once with `-2` option.

To answer a call type `ATA`, to make a call `ATDsomenumber;`, to hangup `ATH`.

That's it. ;)

## Modem reverse engineering

See this page for details.

## Unlock ADB access

It's possible to access the Linux side of the modem via adb, or reboot the modem to fastboot mode and boot your own kernel, The modem is rooted by default, and you can install and run your own software inside the modem. It's possible to communicate between A64 and the modem's ARM CPU via USB serial port (ttyGS0 on modem side and ttyUSB1 on A64).

It's also possible to create your own URCs up to 128B in size by sending them as a datagram message to UNIX socket `/tmp/.urc_sock`. See urc.c.

To install your own program on the modem persistently you need to remount modem's root filesystem read write and install your program somewhere in `/usr/bin`. Add your startup script to `/etc/init.d`.

To get `adb` access to the modem:

- Get adb key via `AT+QADBKEY?`
- Enter the adb key to the unlocker utility
- Follow the instructions from the `qadbkey-unlock`
- Install or get `adb` for your distribution
- `adb shell` should get you a root shell on the modem
- `adb pull` and `adb push` can be used to copy files to/from the modem

On Arch Linux ARM, adb is no longer supported, so you need to compile it from source code. You can use https://github.com/…uong/arm_adb with this patch to make it work with current `openssl`.

## Modem power management

Various PinePhone variants have SoC GPIOs routed to the modem differently. So you need to be aware of what PinePhone variant you have, when experimenting with the modem.

Power management comes down to:

- letting the modem sleep when it's not in use, and waking it up when it's needed for something,
- and letting the host sleep, and configure the modem to wake the host up, when some important event happens (incomming call, sms, …).

Summay of modem's behavior related to powerup/powerdown and power management:

- powerup takes about 14s, unless the modem is too hot (>50–60°C), then it may take as long as 22s due to CPU inside the modem being downclocked from 1.3Ghz to 400MHz
- configuration of the physical UART and ttyUSB2 is shared for some reason, so ATE0 issued on UART will turn echo off also on ttyUSB2
- modem sleeps when an election process based on the status of USB port, DTR pin, etc. results in a positive decision
- modem disables RF based on #W_DISABLE only when configured to do so via `AT+QCFG="airplanecontrol"`
- modem wakes up the host via USB or RI pin based on `AT+QCFG="risignaltype"`
- modem will cache URCs based on `AP_READY` pin's status only when `AT+QCFG="apready"` is configured
- powerdown takes about the same time as powerup