Thales' cellular IoT products business is now part of Telit Cinterion, find out more.

You are here

Telit Cinterion IoT Developer Community

MV31 USB variant RMNET **** and the Linux setup process

Tutorial, April 19, 2021 - 9:13pm, 15656 views

RMNET/QMI is a demanding feature to many customers, that's why it was added to MV31 USB variant in firmware version F0.0.0.5.7. This article describes the process about how to enable it, and the bring-up process when MV31 is running in RMNET mode under Linux.

Preparation on MV31

  • Update the MV31 USB variant module to firmware version equals or greater than F0.0.0.5.7. 
  • Switch the MV31 USB Configuration via an AT command.

Switch the MV31 USB mode to RMNET

The F0.0.0.5.7 firmware added a new AT command AT+USBSWITCH to change the personality of module, please use Teraterm or any terminal emulator program on Windows 10 to apply this command to MV31:

AT+USBSWITCH=00B7 

MV31 will switch to RMNET/QMI mode permanently; you can apply the same command with the parameter 00B3 to switch it back to the MBIM mode.

Preparation on Linux

Let's assume the user will use a Debian-based Linux system on a PC or any single board computer (SBC), in this case any PC/SBC running a recent version of Ubuntu/Debian or Raspberry Pi OS will work if applying below commands.  

Stop the ModemManager

Before testing MV31, please remember to stop ModemManager via:

$ sudo systemctl stop ModemManager

If you want to disable it permanently, you can run this additional command:

$ sudo systemctl disable ModemManager

In this way you won't have to bother stopping it.

Install Additional Packages

The default Ubuntu/Debian installation doesn't provide some required tools, so please run this command to fill-in the gap:

$ sudo apt update && sudo apt install -y libqmi-utils udhcpc

You can download and run mv31_apt script to automate this task.

 

You'll need configure/change two kernel drivers to manage the MV31 in RMNET mode; they are the qmi_wwan for qmi/rmnet and option for ATC/GNSS/Diag ports.

Configure the Built-in Driver

The official kernel built by Ubuntu/Debian or Raspbian already contains the two drivers, so the simplest way is just loading them and apply some configuration.

A temporary setup can be done like this:

$ sudo modprobe qmi_wwan

$ sudo modprobe option

 and configure them via adding the VID PID information into the drivers' new_id field so that they can recognize the MV31:

$ echo '1e2d 00b7' | sudo tee /sys/bus/usb/drivers/qmi_wwan/new_id

$ echo '1e2d 00b7' | sudo tee /sys/bus/usb-serial/drivers/option1/new_id

The benefit of this approach is that it's easy to accomplish, and if your goal was testing RMNET feature, it works well; however the drawback is the qmi_wwan driver can wrongly take over ATC/GNSS ports and mis-configure them as another WAN ports like below:

So with this approach you won't have ATC/GNSS port to access.

A much elaborated way is changing the respective kernel source to include the PID and USB interface information to avoid any possible mis-configuration like the aforementioned one.

Modify the Driver

For option driver, depending on module firmware version and Linux kernel version, the way to modify the option driver (kernel/drivers/usb/serial/option.c,) could be different. That is because a new macro RSVD was introduced to Linux in v4.16.8, before this particular version, to ask option driver to bypass certain USB interfaces, one has to define a struct option_blacklist_info object to reserve given interface(s).

So for Linux kernel greater or equal to this version, the patch looks like below (lines to add beginning with '+'):

#define CINTERION_PRODUCT_AHXX_AUDIO 0x0085

+ #define CINTERION_PRODUCT_BARRACUDA_MBIM 0x00B3
+ #define CINTERION_PRODUCT_BARRACUDA_RMNET 0x00B7
...
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
.driver_info = RSVD(4) },
/* For MV31 with FW < 5.0, add the line below */
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA_MBIM), .driver_info = RSVD(2) | RSVD(1) | RSVD(3) },
/* For MV31 with FW >= 5.0, add the line below */
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA_MBIM), .driver_info = RSVD(0) | RSVD(1) | RSVD(3) },
/* For MV31 with FW >= 5.7, add the line below as well */
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA_RMNET), .driver_info = RSVD(0) },

If the Linux kernel version was before 4.16.8, below lines should work:

#define CINTERION_PRODUCT_AHXX_AUDIO 0x0085

+ #define CINTERION_PRODUCT_BARRACUDA_MBIM 0x00B3
+ #define CINTERION_PRODUCT_BARRACUDA_RMNET 0x00B7
...
/* For MV31 with FW < 5.0 , add the lines below */
+static const struct option_blacklist_info cinterion_mv31_mbim_blacklist123 = {
+ .reserved = BIT(2) | BIT(1) | BIT(3),
+};
...
 { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
 .driver_info = (kernel_ulong_t)&net_intf4_blacklist },/* For MV31 with FW < 5.0 */
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA), .driver_info = (kernel_ulong_t)&cinterion_mv31_mbim_blacklist123 },
/* For MV31 with FW >= 5.0, add the lines below */
+static const struct option_blacklist_info cinterion_mv31_mbim_blacklist013 = {
+ .reserved = BIT(0) | BIT(1) | BIT(3),
+};
/* For MV31 with FW >= 5.7, add the lines below */
+static const struct option_blacklist_info cinterion_mv31_mbim_blacklist0 = {
+ .reserved = BIT(0),
+};
...
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
/* For MV31 with FW < 5.0, add below line */
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA_MBIM), .driver_info = (kernel_ulong_t)&cinterion_mv31_mbim_blacklist123 }, 
/* Otherwise, for MV31 with FW >= 5.0, add below line */
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA_MBIM), .driver_info = (kernel_ulong_t)&cinterion_mv31_mbim_blacklist013 }, 
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_BARRACUDA_RMNET), .driver_info = (kernel_ulong_t)&cinterion_mv31_mbim_blacklist0 }, 

 

After properly patched the source recompile the driver and deploy, then plug in the MV31.

For qmi_wwan driver, please download the corresponding Linux source for the Linux distribution you are running, edit the drivers/net/usb/qmi_wwan.c file under that Linux source tree, add the line labeled with + sign:

...

{QMI_FIXED_INTF(0x1e2d, 0x0083, 4)}, /* Cinterion PHxx,PXxx (1 RmNet + USB Audio)*/
{QMI_QUIRK_SET_DTR(0x1e2d, 0x00b0, 4)}, /* Cinterion CLS8 */
+{QMI_FIXED_INTF(0x1e2d, 0x00b7, 0)}, /* Cinterion MV31 USB variant */

...

them compile and install the modified module.

After that the new driver should be able to correctly identify the MV31 wwan0 and 3 option-based ttyUSBx port like below:

The AT command port will be mapped on /dev/ttyUSB0.

Plug-In MV31 to the Linux Device

After walk through the driver configuration steps, now you can plug-in the MV31 USB module; checking the Linux kernel message, you should find message output like below:

The PID has changed to 00b7, although in the Product Description the MV31 still wrongly call itself as Cinterion PID 0x00B3 USB Mobile Broadband.

If checking the NICs available on the system, you should find a wwan0 port show up:

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

This should return online like below

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

 

if not please try

$ sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-operating-mode='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 AT&T SIM card the output would like this:

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

Both the network handle 938601328 and CID 19 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 10.221.72.243
udhcpc: lease of 10.221.72.243 obtained, lease time 7200

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: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether f0:1f:af:32:95:a1 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.132/24 brd 192.168.0.255 scope global dynamic noprefixroute eno1
valid_lft 81682sec preferred_lft 81682sec
inet6 fe80::5011:eb4e:ab0a:a4fb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: wlp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether ac:b5:7d:34:32:2e brd ff:ff:ff:ff:ff:ff
inet 192.168.0.130/24 brd 192.168.0.255 scope global dynamic noprefixroute wlp2s0
valid_lft 81678sec preferred_lft 81678sec
inet6 fe80::54f:a4c1:c634:1a69/64 scope link noprefixroute
valid_lft forever preferred_lft forever
7: wwan0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/none
inet 10.221.72.243/29 scope global wwan0
valid_lft forever preferred_lft forever
inet6 fe80::d818:dcfa:e1b9:e612/64 scope link stable-privacy
valid_lft forever preferred_lft forever
8: wwan1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 1e:be:e4:44:73:12 brd ff:ff:ff:ff:ff:ff
9: wwan2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 1e:be:e4:44:73:12 brd ff:ff:ff:ff:ff:ff

 

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=938601328 --client-cid=19

 

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

$ sudo ip addr del 10.221.72.243/29 dev wwan0

 

I prepared a bash script: mv31_qmi_test.zip for anyone who is interested to quickly test run these commands, a sample output log from that script is copied below for reference:

libqmi version used:

qmicli 1.24.8
Copyright (C) 2012-2019 Aleksander Morgado
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-software-version
[/dev/cdc-wdm0] Software version: T99W175.F0.0.0.5.7
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --get-wwan-iface
wwan0
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-operating-mode
[/dev/cdc-wdm0] Operating mode retrieved:
Mode: 'online'
HW restricted: 'no'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-ids
[/dev/cdc-wdm0] Device IDs retrieved:
ESN: '0'
IMEI: '351859110037672'
MEID: 'unknown'
IMEI SV: '4'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --dms-get-model
[/dev/cdc-wdm0] Device model retrieved:
Model: 'Thales Snapdragon X55'
Press ENTER to continue...
sudo qmicli --device=/dev/cdc-wdm0 -p --dms-get-manufacturer
[/dev/cdc-wdm0] Device manufacturer retrieved:
Manufacturer: 'Thales'
Press ENTER to continue...
sudo qmicli --device=/dev/cdc-wdm0 -p --dms-get-revision
[/dev/cdc-wdm0] Device revision retrieved:
Revision: '㥔圹㜱⸵う〮〮㔮㜮䄮⹔〰਷㜰‶ㄠ†佛瑣㈠″〲〲ㄠ㨴〰〺崰'
Press ENTER to continue...
sudo qmicli --device=/dev/cdc-wdm0 -p --uim-get-card-status
[/dev/cdc-wdm0] Successfully got card status
Provisioning applications:
Primary GW: slot '1', application '1'
Primary 1X: session doesn't exist
Secondary GW: session doesn't exist
Secondary 1X: session doesn't exist
Slot [1]:
Card state: 'present'
UPIN state: 'not-initialized'
UPIN retries: '0'
UPUK retries: '0'
Application [1]:
Application type: 'usim (2)'
Application state: 'ready'
Application ID:
A0:00:00:00:87:10:02:FF:FF:FF:FF:89:03:02:00:00
Personalization state: 'ready'
UPIN replaces PIN1: 'no'
PIN1 state: 'disabled'
PIN1 retries: '3'
PUK1 retries: '10'
PIN2 state: 'enabled-not-verified'
PIN2 retries: '3'
PUK2 retries: '10'
Application [2]:
Application type: 'isim (5)'
Application state: 'detected'
Application ID:
A0:00:00:00:87:10:04:FF:FF:FF:FF:89:03:02:00:00
Personalization state: 'unknown'
UPIN replaces PIN1: 'no'
PIN1 state: 'disabled'
PIN1 retries: '3'
PUK1 retries: '10'
PIN2 state: 'not-initialized'
PIN2 retries: '0'
PUK2 retries: '0'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --nas-get-serving-system
[/dev/cdc-wdm0] Successfully got serving system:
Registration state: 'registered'
CS: 'attached'
PS: 'attached'
Selected network: '3gpp'
Radio interfaces: '1'
[0]: 'lte'
Roaming status: 'off'
Data service capabilities: '1'
[0]: 'lte'
Current PLMN:
MCC: '310'
MNC: '410'
Description: 'AT&T'
Roaming indicators: '1'
[0]: 'off' (lte)
3GPP time zone offset: '-480' minutes
3GPP daylight saving time adjustment: '0' hours
3GPP cell ID: '107898902'
Detailed status:
Status: 'available'
Capability: 'cs-ps'
HDR Status: 'none'
HDR Hybrid: 'no'
Forbidden: 'no'
LTE tracking area code: '37143'
Full operator code info:
MCC: '310'
MNC: '410'
MNC with PCS digit: 'yes'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --nas-get-signal-info
[/dev/cdc-wdm0] Successfully got signal info
LTE:
RSSI: '-54 dBm'
RSRQ: '-18 dB'
RSRP: '-92 dBm'
SNR: '10.0 dB'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --nas-get-system-info
[/dev/cdc-wdm0] Successfully got system info:
WCDMA service:
Status: 'none'
True Status: 'none'
Preferred data path: 'no'
LTE service:
Status: 'available'
True Status: 'available'
Preferred data path: 'no'
Domain: 'cs-ps'
Service capability: 'cs-ps'
Roaming status: 'off'
Forbidden: 'no'
Cell ID: '107898902'
MCC: '310'
MNC: '410'
Tracking Area Code: '37143'
Voice support: 'no'
IMS voice support: 'yes'
eMBMS coverage info support: 'no'
eMBMS coverage info trace ID: '65535'
Cell access: 'all-calls'
Registration restriction: 'unrestricted'
Registration domain: 'not-applicable'
SIM reject info: 'available'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --get-expected-data-format
802-3
Press ENTER to continue...
sudo ip link set wwan0 down
Press ENTER to continue...
echo 'Y' | sudo tee /sys/class/net/wwan0/qmi/raw_ip
Y
Press ENTER to continue...
sudo ip link set wwan0 up
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --wds-start-network="ip-type=4,apn=broadband" --client-no-release-cid
[/dev/cdc-wdm0] Network started
Packet data handle: '4010122464'
[/dev/cdc-wdm0] Client ID not released:
Service: 'wds'
CID: '19'
Press ENTER to continue...
sudo qmicli -d /dev/cdc-wdm0 -p --wds-get-current-settings
[/dev/cdc-wdm0] Current settings retrieved:
IP Family: IPv4
IPv4 address: 10.104.71.12
IPv4 subnet mask: 255.255.255.248
IPv4 gateway address: 10.104.71.13
IPv4 primary DNS: 172.26.38.1
MTU: 1430
Domains: none
Press ENTER to continue...
sudo udhcpc -q -f -i wwan0
udhcpc: started, v1.30.1
udhcpc: sending discover
udhcpc: sending select for 10.104.71.12
udhcpc: lease of 10.104.71.12 obtained, lease time 7200
Press ENTER to continue...
Current Linux IP configuration: 
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: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether f0:1f:af:32:95:a1 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.132/24 brd 192.168.0.255 scope global dynamic noprefixroute eno1
valid_lft 73025sec preferred_lft 73025sec
inet6 fe80::5011:eb4e:ab0a:a4fb/64 scope link noprefixroute 
valid_lft forever preferred_lft forever
3: wlp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether ac:b5:7d:34:32:2e brd ff:ff:ff:ff:ff:ff
inet 192.168.0.130/24 brd 192.168.0.255 scope global dynamic noprefixroute wlp2s0
valid_lft 73021sec preferred_lft 73021sec
inet6 fe80::54f:a4c1:c634:1a69/64 scope link noprefixroute 
valid_lft forever preferred_lft forever
4: wwan0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/none 
inet 10.104.71.12/29 scope global wwan0
valid_lft forever preferred_lft forever
inet6 fe80::7fa6:c522:4ac7:9038/64 scope link stable-privacy 
valid_lft forever preferred_lft forever
default via 10.104.71.13 dev wwan0 
default via 192.168.0.1 dev eno1 proto dhcp metric 100 
default via 192.168.0.1 dev wlp2s0 proto dhcp metric 600 
10.104.71.8/29 dev wwan0 proto kernel scope link src 10.104.71.12 
169.254.0.0/16 dev wlp2s0 scope link metric 1000 
192.168.0.0/24 dev eno1 proto kernel scope link src 192.168.0.132 metric 100 
192.168.0.0/24 dev wlp2s0 proto kernel scope link src 192.168.0.130 metric 600 
Press ENTER to continue...
Ping test via wwan0:
PING 8.8.8.8 (8.8.8.8) from 10.104.71.12 wwan0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=75.0 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=114 time=70.9 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=114 time=67.0 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=114 time=61.0 ms
...

References:

You can refer to below sites for qmicli related information:

https://www.freedesktop.org/software/libqmi/man/latest/qmicli.1.html

 

Is there a plan to describe the setup process for PCIe (MHI) variant of the MV31-W modem?

Where we can find a support for that version?

PCIe variant requires deeper knowledge for kernel integration, better use a dedicate CSP to drive HQ support.

Best Regards,
Antony Shen

Is there any difference in driver modification for USB and PCIe version of MV31 module?

PCIe version require MHA driver to work with the MV31, please contact TS for detail.

Best Regards,
Antony Shen

Author

antonyshen's picture
antonyshen