2

Lenovo ships its P16 gen 2 laptops (certified for Ubuntu linux) with the following wwan card: Intel MBIM [8086:7560]. Modem manager see the wwan modem, but cannot enable it (error message: ** (modem-manager-gui:9178): WARNING **: 10:33:30.084: Modem Manager >= 0.7.0: GDBus.Error:org.freedesktop.ModemManager1.Error.Core.Retry: Invalid transition

According to Lenovo instructions (ThinkPad P16 Gen 2 Linux User guide: one should "Go to https://support.lenovo.com and select the entry for your computer. Download the wwan-linux-fcc- unlock package from the product support page. Make sure to check the README to confirm if any restrictions for your geography apply, and to get instructions on how to install the application.)

However there is unfortunately no such file available on the lenovo sites. And there is also no comment from Lenovo on this failure to provide the fcc unlock file.

This link also appears to be empty: https://www.lenovo.com/linux/wwan-enablement-on-Linux.pdf

Unless anyone knows where the fcc ulock file for the abovementioned wwan card can be found, the wwan modem in the P16 series is not usable in ubuntu or linux for that matter. Is there any information about this around?

thanks in advance

3 Answers3

1

For the 8086:7560 unlock I was successfully able to unlock on the ThinkPad X1 Yoga Gen 7 using the script that is located here: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/751.

You can store the script as /etc/ModemManager/fcc-unlock.d/8086:7560 and make sure it is executable (chmod +x). You will also need to change the header's shebang from python to python3.

There are going to be some workarounds needed especially if you use suspend/hibernate, since the modem itself is rather finicky.

One major issue is that once the modem is unpowered on the PCI bus it will not be able to be powered up again, needing to be turned on again. This affects the modem itself and the bus it is attached to.

You will need the following udev rule:

/etc/udev/rules.d/99-modem-suspend.rules

# The modem fails to wake up ever again after suspend
SUBSYSTEM=="pci", ATTR{vendor}=="0x1cf8", ATTR{device}=="0x8653", ATTR{power/control}="on", GOTO="pci_pm_end"
SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0x7560", ATTR{power/control}="on", GOTO="pci_pm_end"
SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0x51b8", ATTR{power/control}="on", GOTO="pci_pm_end"

Use normal sleeping otherwise

SUBSYSTEM=="pci", ATTR{power/control}="auto" LABEL="pci_pm_end"

Then you will need a script that runs at boot to set some initial properties of the device power saving so that the modem or Linux does not suspend the device but keeps it always powered.

/opt/keep-modem-awake.sh

#!/bin/sh

Enable wakeups

Bus the modem is on

if grep 'pci:0000:00:1c.0' < /proc/acpi/wakeup | grep disabled then echo "Enabling wakeup for bus owning Modem..." > /dev/kmsg echo RP01 > /proc/acpi/wakeup fi

Modem

if grep 'pci:0000:08:00.0' < /proc/acpi/wakeup | grep disabled then echo "Enabling wakeup for modem..." > /dev/kmsg echo PXSX > /proc/acpi/wakeup fi

Disable d3cold for the modem

It is behind the bridge: 00:1c.0 PCI bridge: Intel Corporation Device 51b8 (rev 01)

https://patchwork.kernel.org/project/linux-pci/patch/20210329052339.20882-1-kai.heng.feng@canonical.com/

echo "Disabling modem d3cold..." > /dev/kmsg

The actual modem

echo 0 > /sys/bus/pci/devices/0000:08:00.0/d3cold_allowed

The owning buses

echo 0 > /sys/devices/pci0000:00/0000:00:1c.0/d3cold_allowed echo 0 > /sys/bus/acpi/devices/PNP0A08:00/device:4f/physical_node/d3cold_allowed echo 0 > /sys/bus/acpi/devices/PNP0A08:00/device:4f/device:50/physical_node/d3cold_allowed

Use ACPI to reset the PCI and not just power off and power on the device

echo "Setting modem reset method to ACPI..." > /dev/kmsg echo acpi > /sys/bus/pci/devices/0000:08:00.0/reset_method

NOTE: your system might have different device paths. For example, on Lenovo ThinkPad Z16 Gen 1 the modem is 0000:05:00.0, the owning bus is 0000:00:01.3 and ACPI devices PNP0A08:00/device:07/device:08. To properly identify which IDs to use you can do the following:

  1. Identify your modem with lspci command. For example:
❯ lspci | grep Modem
05:00.0 Wireless controller [0d40]: Intel Corporation XMM7560 LTE Advanced Pro Modem (rev 01)

This gives us a device path /sys/bus/pci/devices/0000:05:00.0

  1. Identify owning bus:
❯ ls -l /sys/bus/pci/devices/0000:05:00.0
lrwxrwxrwx. 1 root root 0 Apr 14 10:24 /sys/bus/pci/devices/0000:05:00.0 -> ../../../devices/pci0000:00/0000:00:01.3/0000:05:00.0

Gives us owning bus path /sys/bus/pci/devices/0000:00:01.3

  1. Identify ACPI owning buses:
❯ find /sys/bus/acpi/devices/PNP0A08:00/ -type l -exec ls -l {} + | grep '05:00.0'

lrwxrwxrwx. 1 root root 0 Apr 14 14:25 /sys/bus/acpi/devices/PNP0A08:00/device:07/device:08/physical_node -> ../../../../../pci0000:00/0000:00:01.3/0000:05:00.0

Gives us paths /sys/bus/acpi/devices/PNP0A08:00/device:07 and /sys/bus/acpi/devices/PNP0A08:00/device:07/device:08

Then for running the script at boot:

/etc/systemd/system/keep-modem-awake.service

[Unit]
Description="Keep modem awake for suspend."
After=ModemManager.service NetworkManager.service
Wants=ModemManager.service NetworkManager.service

[Service] ExecStart=/opt/keep-modem-awake.sh

[Install] WantedBy=multi-user.target

For later scripts, you will need a script that can find the modem because when the system suspends and resumes, the modem ID will change each time.

/opt/find-modem.sh

#!/bin/sh

Make sure path is set

export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

for __i in $(seq 1 10) do __modem="$(/opt/find-modem-sub.sh | head -n 1)" if [ -z "$__modem" ] then sleep 0.5 else break fi done

if [ -z "$__modem" ] then exit 1 fi

echo "$__modem" exit 0

/opt/find-modem-sub.sh

#!/bin/sh

Make sure path is set

export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Read in commands

mmcli -L | grep '/Modem/' | while read __line do echo "$__line" | sed 's/^./Modem/([0-9]{1,}).$/\1/' exit 0 done

exit 1

Then, if you plan to use suspend and hibernate, you will have to setup scripts accordingly for that so that they run before and after that occurs:

/opt/suspend-modem.sh

#!/bin/sh

Make sure path is correct

export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Get modem ID

__modem="$(/opt/find-modem.sh)"

if [ "$1" = "0" ] then echo "Telling NM to not use the modem..." > /dev/kmsg nmcli c down NetworkName

    # Make sure the connection is deactivated
    echo &quot;Waiting for NM to show disconnected...&quot; &gt; /dev/kmsg
    while nmcli con show --active | grep NetworkName
    do
            sleep 0.5
    done

    # Deactivate the modem before suspend as the connection freezes and never comes back
    echo &quot;Disabling modem...&quot; &gt; /dev/kmsg
    mmcli -m &quot;$__modem&quot; --disable

elif [ "$1" = "1" ] then echo "Disabling modem after resume..." > /dev/kmsg mmcli -m "$__modem" --disable

    echo &quot;Entering low power mode after disable...&quot; &gt; /dev/kmsg
    mmcli -m &quot;$__modem&quot; --set-power-state-low

    echo &quot;Performing enabling loops...&quot; &gt; /dev/kmsg
    for __i in $(seq 1 5)
    do
            echo &quot;Loop $__i...&quot; &gt; /dev/kmsg

            echo &quot;Turning on modem and enabling...&quot; 1&gt;&amp;2
            mmcli -m &quot;$__modem&quot; --set-power-state-on
            if mmcli -m &quot;$__modem&quot; --enable
            then
                    echo &quot;Modem was enabled...&quot; &gt; /dev/kmsg
                    break
            else
                    echo &quot;Did not enable modem...&quot; &gt; /dev/kmsg
                    sleep 0.5
            fi
    done

    echo &quot;Telling NM to use the modem now...&quot; &gt; /dev/kmsg
    nmcli c up NetworkName

fi

Then accordingly the following systemd, this one for resume:

rfkill-modem-resume.service

[Unit]
Description=Enable modem after wake-up
After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

[Service] Type=simple ExecStart=/opt/suspend-modem.sh 1

[Install] WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

And this one for suspend:

rfkill-modem-suspend.service

[Unit]
Description=rfkill modem before sleep
Before=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

[Service] Type=simple ExecStart=/opt/suspend-modem.sh 0

[Install] WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

With these scripts and otherwise, I have had success in keeping the modem alive after multiple suspend and hibernates.

As an extra, you may also want to adjust the NetworkManager connection so that DNS and route priority are lower (higher values) than your main connection so that it is used as a fallback.

0

There is also a pending merge request with a bash-based unlock script.

I made a modified version of it that also sends AT+XDNS=0,1 command because without it the connection keeps dropping.

0

Lenovo recently released this repo for unlocking WWAN module: https://github.com/lenovo/lenovo-wwan-unlock

Works really well, sometimes I have to run sudo service ModemManager restart but other than that, it's great and I'm really happy having this working on Ubuntu 24.04.

petur
  • 161