Gemalto is now part of the Thales Group, find out more.

You are here

Thales IoT Developer Community

PLS63/83 QMI/RMNET Setup and Linux qmi_wwan / ModemManager Integration

Tutorial, April 19, 2021 - 11:00pm, 9151 views

This article shows how to configure a PLS63/PLS83 to operate in QMI/RMNET **** and the bring up procedure for Linux.

Background knowledge

PLS63/PLS83 supports both CDC-ECM and QMI/RMNET, either one can be active; but to have LTE Cat 4 throughput on PLS83 one must use the QMI/RMNET ****.

The AT^SSRVSET="actSrvSet"\[, <SetNum>\] command can be used to switch between ECM (<SetNum>=1 for PID==0069) and this QMI WWAN(<SetNum>=11 for PID==006F).

Preparation

Before trying with Linux, please use any termial emulator tool to configure the module into QMI/RMNET **** via the two commands listed bellow:

    at^ssrvset=“actSrvset",11
    at+cfun=1,1

On Linux machine, use these commands to enable QMI/RMNET support on PLSx3:

$ sudo apt update && sudo apt install -y libqmi-utils udhcpc
$ sudo modprobe qmi_wwan
$ echo '1e2d 006F' | sudo tee /sys/bus/usb/drivers/qmi_wwan/new_id

After these steps, you can reset the PLSx3 on Linux or re-plugin it to the Linux machine. The Linux kernel would see the PLSx3 and enumerate to 4 ACM and 1 RMNET ports:

Dec 15 13:17:47 usfrpi4 kernel: [334270.828196] usb 1-1.4.4.3: new high-speed USB device number 16 using xhci_hcd
Dec 15 13:17:47 usfrpi4 kernel: [334271.030110] usb 1-1.4.4.3: New USB device found, idVendor=1e2d, idProduct=006f, bcdDevice= 0.00
Dec 15 13:17:47 usfrpi4 kernel: [334271.030128] usb 1-1.4.4.3: New USB device strings: Mfr=3, Product=2, SerialNumber=4
Dec 15 13:17:47 usfrpi4 kernel: [334271.030140] usb 1-1.4.4.3: Product: PLSx3
Dec 15 13:17:47 usfrpi4 kernel: [334271.030151] usb 1-1.4.4.3: Manufacturer: Cinterion Wireless Modules
Dec 15 13:17:47 usfrpi4 kernel: [334271.030162] usb 1-1.4.4.3: SerialNumber: 1fc6a84f
Dec 15 13:17:47 usfrpi4 kernel: [334271.041614] cdc_acm 1-1.4.4.3:1.0: ttyACM0: USB ACM device
Dec 15 13:17:47 usfrpi4 kernel: [334271.043509] cdc_acm 1-1.4.4.3:1.2: ttyACM1: USB ACM device
Dec 15 13:17:47 usfrpi4 kernel: [334271.045766] cdc_acm 1-1.4.4.3:1.4: ttyACM2: USB ACM device
Dec 15 13:17:47 usfrpi4 kernel: [334271.048003] cdc_acm 1-1.4.4.3:1.6: ttyACM3: USB ACM device
Dec 15 13:17:47 usfrpi4 kernel: [334271.049933] qmi_wwan 1-1.4.4.3:1.8: cdc-wdm0: USB WDM device
Dec 15 13:17:47 usfrpi4 kernel: [334271.053273] qmi_wwan 1-1.4.4.3:1.8 wwan0: register 'qmi_wwan' at usb-0000:01:00.0-1.4.4.3, WWAN/QMI device, 8a:a0:ff:ef:de:d4

Bring up the wwan0

Once the rmnet driver is up and running, we'll use qmicli as the primary tool to bring-up the wwan0. To learn which QMI command is provided by this tool, please visit https://www.freedesktop.org/software/libqmi/man/latest/qmicli.1.html for detail. Below is an example of useful commands for common usage scenario.

To make sure the module is ready, one can test it with the following command:

$ sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-operating-****

This should return online like below

[/dev/cdc-wdm0] Operating **** retrieved:
****: 'online'
HW restricted: 'no'

 

if not please try

$ sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-operating-****='online'

 

If a SIM pin is required for the SIM card, use command bellow:

$ sudo qmicli --device=/dev/cdc-wdm0 -p --dms-uim-verify-pin=PIN,1234

 

The name of the related network interface to QMI control channel can be acquired with the command:

$ sudo qmicli -d /dev/cdc-wdm0 -p --get-wwan-iface

wwan0

Now configure the network interface to run via the raw-ip protocol:

$ sudo ip link set wwan0 down

$ echo 'Y' | sudo tee /sys/class/net/wwan0/qmi/raw_ip

$ sudo ip link set wwan0 up

 

Once the wwan0 is up, connect the mobile network by changing the apn='YOUR_APN',username='YOUR_USERNAME',password='YOUR_PASSWORD' part of the line according to the information of your SIM & operator:

$ sudo qmicli -p -d /dev/cdc-wdm0 -p --wds-start-network="apn='YOUR_APN',username='YOUR_USERNAME',password='YOUR_PASSWORD',ip-type=4" --client-no-release-cid

 

When using a VzW SIM card the command & output would look like this:

$ sudo qmicli -d /dev/cdc-wdm0 -p --wds-start-network="ip-type=4,apn=vzwinternet" --client-no-release-cid
[/dev/cdc-wdm0] Network started
Packet data handle: '2244170208'
[/dev/cdc-wdm0] Client ID not released:
Service: 'wds'
CID: '3'

Both the network handle 2244170208 and CID 3 are important because they'll be needed for --wds-stop-network command so please take note here.

Once the network was started, you can send a DHCP request on the network interface. Please note that not all DHCP clients in Linux can support Raw-IP format, udhcpc however support this for IPv4 over Raw-IP. Please apply this command to request DHCP configuration on wwan0:

$ sudo udhcpc -q -f -i wwan0
udhcpc: started, v1.30.1
udhcpc: sending discover
udhcpc: sending select for 100.89.179.219
udhcpc: lease of 100.89.179.219 obtained, lease time 7200
ip: RTNETLINK answers: File exists

Up until now the wwan0 has been up and running, you may check it's assignment via 

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether dc:a6:32:19:47:38 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.200/24 brd 192.168.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.0.110/24 brd 192.168.0.255 scope global secondary eth0
valid_lft forever preferred_lft forever
inet6 fe80::dea6:32ff:fe19:4738/64 scope link
valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether dc:a6:32:19:47:39 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.103/24 brd 192.168.0.255 scope global dynamic wlan0
valid_lft 85433sec preferred_lft 85433sec
inet6 fe80::dea6:32ff:fe19:4739/64 scope link
valid_lft forever preferred_lft forever
5: wwan0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/none
inet 100.89.179.219/29 scope global wwan0
valid_lft forever preferred_lft forever
inet6 fe80::9ea0:3a60:ad31:6fcd/64 scope link stable-privacy
valid_lft forever preferred_lft forever

 

To verify if the wwan0 was up and running, you can ping an Internet host like below:

$ ping -I wwan0 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 100.89.179.219 wwan0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=227 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=114 time=47.6 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=114 time=44.8 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=114 time=42.0 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=114 time=42.3 ms
^C
--- 8.8.8.8 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/***/mdev = 41.990/80.642/226.516/72.964 ms

 

To bring-down wwan0, you'll need to providing the network handle and CID returned at connection activation:
$ sudo qmicli --device=/dev/cdc-wdm0 -p --wds-stop-network=NETWORK_HANDLE --client-cid=CID

 

As aforementioned case, we can run below command the stop the rmnet:

$ sudo qmicli --device=/dev/cdc-wdm0 -p --wds-stop-network=2244170208 --client-cid=3


 

Don't forget to remove the IP associated with wwan0:

$ sudo ip addr del 100.89.179.219/29 dev wwan0

 

Linux ****mManager Support

On a PC running Ubuntu Linux, simply add these lines to /lib/udev/rules.d/77-mm-cinterion-port-types.rules 

# PLS83 family enumeration
# ttyACM0 (if #0): ****m port
# ttyACM1 (if #2): AT port
# ttyACM2 (if #4): AT port
# ttyACM3 (if #6): reserved
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"

ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"

 

and ask udev ****** to reload the rule:

$ sudo udevadm control -R

 

Ensure the qmi_wwan driver was loaded and configured via the two commands:

$ sudo modprobe qmi_wwan
$ echo '1e2d 006F' | sudo tee /sys/bus/usb/drivers/qmi_wwan/new_id

Then configure the Mobile Broadband Network via the Network Manager GUI.

 

The Network Manager will invoke ****mManager and bring-up the cellular network, everything just works.

Hi,

I did all the commands and everything seem fine but pinging from wwan0 to google doesn't work, even though I have the same output as you for the ip a command.

Also, I am not sure what you mean by 

Then configure the Mobile Broadband Network via the Network Manager GUI.

The Network Manager will invoke ****mManager and bring-up the cellular network, everything just works.

Where can I find the Network Manager GUI and what should I configure ?

 

Thank you for your answer,

Best Regards

 

A Random Intern

I assume your test was on a PC running some Linux distro like Ubuntu right? If that's the case, the DNS resolver nowadays has been replaced to systemd-resolved, this one doesn't compatible with udhcpc, you need to customize the default script offered by udhcpc to adopt with it.

So my solution is that modify the default.script offered by udhcpc package to this one:

#!/bin/sh
# Busybox udhcpc dispatcher script.
# Copyright (C) 2009 by Axel Beckert.
# Copyright (C) 2014 by Michael Tokarev.
# Copyright (C) 2021 by Antony Shen.
#
# Based on the busybox example scripts and the old udhcp source
# package default.* scripts.

RESOLV_CONF="/etc/resolv.conf"

log() {
    logger -t "udhcpc[$PPID]" -p ******.$1 "$interface: $2"
}

echo "PARAM: $1"
case $1 in
    bound|renew)

    # Configure new IP address.
    # Do it unconditionally even if the address hasn't changed,
    # to also set subnet, broadcast, mtu, ...
    echo "ifconfig $interface ${mtu:+mtu $mtu} $ip netmask $subnet ${broadcast:+broadcast $broadcast}"
    echo "domain=$domain"
    echo "dns=$dns"

    busybox ifconfig $interface ${mtu:+mtu $mtu} \
        $ip netmask $subnet ${broadcast:+broadcast $broadcast}

    # get current ("old") routes (after setting new IP)
    crouter=$(busybox ip -4 route show dev $interface |
              busybox awk '$1 == "default" { print $3; }')
    router="${router%% *}" # linux kernel supports only one (default) route
    if [ ".$router" != ".$crouter" ]; then
        # reset just default routes
        echo "ip -4 route flush exact 0.0.0.0/0 dev $interface"
        busybox ip -4 route flush exact 0.0.0.0/0 dev $interface
    fi
    if [ -n "$router" ]; then
        # special case for /32 subnets: use onlink keyword
        [ ".$subnet" = .255.255.255.255 ] \
            && onlink=onlink || onlink=
        echo "ip -4 route add default via $router dev $interface $onlink"
        busybox ip -4 route add default via $router dev $interface $onlink
    fi

    # Update resolver configuration file
    [ -n "$domain" ] && R="domain $domain" || R=""
    for i in $dns; do
        R="$R
nameserver $i"
    done

    if [ -x /sbin/resolvconf ]; then
        echo "$R" | resolvconf -a "$interface.udhcpc"
    else
        systemd-resolve --interface $interface --set-dns $i 
    fi

    log info "$1: IP=$ip/$subnet router=$router domain=\"$domain\" dns=\"$dns\" lease=$lease"
    ;;

    deconfig)
    busybox ip link set $interface up
    busybox ip -4 addr flush dev $interface
    busybox ip -4 route flush dev $interface
    [ -x /sbin/resolvconf ] &&
        resolvconf -d "$interface.udhcpc"
    log notice "deconfigured"
    ;;

    leasefail | nak)
    log err "configuration failed: $1: $message"
    ;;

    *)
    echo "$0: Unknown udhcpc command: $1" >&2
    exit 1
    ;;
esac

 

then chmod +x this script; if you named it as udhcpc-systemd.script, run 

chmod a+x udhcpc-systemd.script

 

then run 

sudo udhcpc -q -f -s ./udhcpc-systemd.script -i wwan0 

 

This should be able to configure systemd-resolved properly.

Best Regards,
Antony Shen

Hi Anthony,

great modification of the udhcpc script.
What I miss, is the MTU size setting, which is not taken over from the qmi part (always 1500 is set).
Therefore I added this right below the echo  domain and echo dns part of your script:
    ...
    echo "dns=$dns"
    echo "mtu=$mtu"
    if [ -z $mtu ]; then
       echo ">> No MTU - retrieving mtu from qmicli"
       mtu=$(qmicli -d /dev/cdc-wdm0  --wds-get-current-settings | grep MTU | awk '{print $2}') 
    fi
    echo "mtu=$mtu"
   ....

with that, the IF wwan0 will have the correct MTU setting.
It might be improved, as I set the /dev/cdcwdm to a fixed value.

Author

antonyshen's picture
antonyshen