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
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.