/*nice*/
顯示具有 Cellular 標籤的文章。 顯示所有文章
顯示具有 Cellular 標籤的文章。 顯示所有文章

2026年4月22日 星期三

Remove all the COM/Modem ports on Win10

Open Powershell

Remove all the COM ports
PS C:\Users\ubuntu> Get-PnpDevice | Where-Object { $_.Class -eq "Ports" -and $_.Present -eq $false } | ForEach-Object { pnputil /remove-device $_.InstanceId }
Microsoft PnP 公用程式

正在移除裝置:          FTDIBUS\VID_0403+PID_6011+09001550C\0000
已順利移除裝置。


a.   Get-PnpDevice: 獲取所有裝置列表。
b.   $_.Class -eq "Ports": 篩選出屬於「連接埠 (COM 和 LPT)」的裝置。
c.   $_.Present -eq $false: 篩選出「目前不在線上(隱藏)」的裝置。
d.   pnputil /remove-device: 強制移除這些裝置

Remove all the Modem ports
PS C:\Users\ubuntu> Get-PnpDevice | Where-Object { $_.Class -eq "Modem" -and $_.Present -eq $false } | ForEach-Object { pnputil /remove-device $_.InstanceId }
Microsoft PnP 公用程式

正在移除裝置:          USB\VID_1BC7&PID_1031&MI_02\6&170AB803&1&0002
已順利移除裝置。2

2026年4月8日 星期三

Telit LE910Q1 ECM Mode

LE910Q1
ECM Mode, VID: 7021
$ lsusb
Bus 003 Device 009: ID 1bc7:7021 Telit Wireless Solutions TelitCinterion Compo

Get IPv6 only via AT command
at+cgdcont=1,"IPv6",""
OK


at+cgpaddr=1
+CGPADDR: 1,"32.1.180.0.226.169.68.63.134.237.190.6.38.2.50.16"


AT#SGACT=1,1
OK

AT#PING="2001:4860:4860::8888"
#PING: 01,32.1.72.96.72.96.0.0.0.0.0.0.0.0.136.136,1,116
#PING: 02,32.1.72.96.72.96.0.0.0.0.0.0.0.0.136.136,0,116
#PING: 03,32.1.72.96.72.96.0.0.0.0.0.0.0.0.136.136,0,116
#PING: 04,32.1.72.96.72.96.0.0.0.0.0.0.0.0.136.136,0,116
OK

Set RA config
強制觸發: 有時候需要手動開啟內核對 RA (Router Advertisement) 的接收:
accept_ra: 決定是否接收來自基地台(電信商)的路由器公告訊息(Router Advertisement)。
autoconf: 決定是否根據收到的 RA 訊息,自動產生 IPv6 位址(SLAAC 流程)。

$ sudo sysctl -w net.ipv6.conf.wwan0.accept_ra=2 
    0:關閉。不接受 Router Advertisement (RA)。
    1:預設。如果該網卡沒有開啟轉發 (forwarding=0),則接受 RA。
    2:強制開啟。即使該網卡開啟了轉發 (forwarding=1),依然接受 RA。

$ sudo sysctl -w net.ipv6.conf.wwan0.autoconf=1

$ cat /proc/sys/net/ipv6/conf/wwan0/accept_ra

$ cat /proc/sys/net/ipv6/conf/wwan0/auto

$ reboot

Enable the wireless card


$ sudo ifconfig wwx2026123aa up

$ ifconfig wwx2026123aa

wwx2026123aa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 2001:b400:e2a9:ffff:ffff:ffff:ffff:cc88  prefixlen 64  scopeid 0x0<global>
        inet6 2001:b400:e2a9:ffff:ffff:ffff:ffff:96aa  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::2289:84ff:fe6a:96aa  prefixlen 64  scopeid 0x20<link>
        ether 11:11:11:11:11:11  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

應該能直接得到IPv6 address
模組透過ECM Mode計算後,分配兩組IP(無狀態自動配置(SLAAC))


ECM 模式與 IP 分配邏輯
在 CDC-ECM 模式下,模組會模擬成一張標準的乙太網路卡。當介面 up 之後,流程如下:
1. RS/RA 封包交換: 主機發送 Router Solicitation (RS),模組(或電信端網路)回覆 Router Advertisement (RA)。
2. Prefix 下發: RA 封包會提供一個 /64 的網路前綴(Prefix),例如你畫面中的 2001:b400:e2a9:443f::/64。
3. SLAAC 計算:
主機根據這個 Prefix,結合自身的 MAC 位址或隨機數,自動計算出 Global 廣域位址。
由於 Linux 核心通常預設開啟了 IPv6 Privacy Extensions (RFC 4941),所以會同時產生一組「固定」與一組「臨時」的位址,這就是你看到「兩組全球 IP」的原因。

類型 位址範例 說明
Global (隱私位址) 2001:b400:...:cc88 臨時性位址 (Temporary Address)。為了保護隱私,系統會隨機產生一組位址用於對外連線,避免追蹤。
Global (固定位址) 2001:b400:...:96aa 公共位址 (Public Address)。由 Prefix + 介面識別碼組成,通常較固定,可用於被動接收連線。
Link-Local fe80::...:96aa 區域連結位址。僅用於本地端(模組與主機之間)的通訊,不跨越路由器。



Set the IPv6 route
$ ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2001:1111:2222:3333::/64 dev wwx2026123aa proto kernel metric 256 pref medium
fe80::/64 dev wwx2089846a96aa proto kernel metric 256 pref medium
default via fe80::aaaa:bbbb:cccc:e685 dev wwx2089846a96aa proto ra metric 1024 expires 65525sec hoplimit 255 pref high

$ sudo ip -6 rule add from 2001:1111:2222:3333::/64 lookup 100

$ sudo ip -6 route replace default via fe80::aaaa:bbbb:cccc:e685 dev wwx2026123aa table 100

$ sudo ip -6 route add default via fe80::aaaa:bbbb:cccc:e685 dev wwx2026123aa metric 100



$ ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2001:1111:2222:3333::/64 dev wwx2089846a96aa proto kernel metric 256 pref medium
fe80::/64 dev wwx2089846a96aa proto kernel metric 256 pref medium
default via fe80::aaaa:bbbb:cccc:e685 dev wwx2089846a96aa metric 100 pref medium
default via fe80::aaaa:bbbb:cccc:e685 dev wwx2089846a96aa proto ra metric 1024 expires 65507sec hoplimit 255 pref high


$ ping6 -I wwx2089846a96aa 2001:4860:4860::8888
PING 2001:4860:4860::8888(2001:4860:4860::8888) from 2001:b400:e255:a973:8836:4d92:92bd:f430 wwx2089846a96aa: 56 data bytes
64 bytes from 2001:4860:4860::8888: icmp_seq=1 ttl=115 time=43.1 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=2 ttl=115 time=64.6 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=3 ttl=115 time=37.9 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=4 ttl=115 time=37.1 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=5 ttl=115 time=55.8 ms
^C
--- 2001:4860:4860::8888 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4004ms
rtt min/avg/max/mdev = 37.056/47.690/64.591/10.783 ms


$ ifconfig wwx2026123aa

wwx2026123aa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 2001:b400:e2a9:aaaa:bbbb:cccc:dddd:cc88  prefixlen 64  scopeid 0x0<global>
        inet6 2001:b400:e2a9:ffff:ffff:ffff:ffff:96aa  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::2289:84ff:fe6a:96aa  prefixlen 64  scopeid 0x20<link>
        ether 11:11:11:11:11:11  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


$ ping6 -I 2001:b400:e2a9:aaaa:bbbb:cccc:dddd:cc88 2001:4860:4860::8888
PING 2001:4860:4860::8888(2001:4860:4860::8888) from 2001:b400:e255:1111:2222:3333:4444:5555 : 56 data bytes
64 bytes from 2001:4860:4860::8888: icmp_seq=1 ttl=114 time=153 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=2 ttl=115 time=52.4 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=3 ttl=115 time=51.1 ms
64 bytes from 2001:4860:4860::8888: icmp_seq=4 ttl=115 time=52.5 ms
^C
--- 2001:4860:4860::8888 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 51.108/77.171/152.633/43.571 ms


Set DNS
$sudo resolvectl dns wwx2026123aa 2001:4860:4860::8888 2001:4860:4860::8844

#Tell the system this interface is the "default" for all DNS queries
$ sudo resolvectl domain wwx2026123aa "~."

$ resolvectl status wwx2026123aa
Link 5 (wwx2026123aa)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 2001:4860:4860::8844
       DNS Servers: 2001:4860:4860::8888 2001:4860:4860::8844
        DNS Domain: ~.


$ ping6 google.com
PING google.com(lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e)) 56 data bytes
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=1 ttl=116 time=41.8 ms
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=2 ttl=116 time=41.3 ms
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=3 ttl=116 time=28.7 ms
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=4 ttl=116 time=21.6 ms
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=5 ttl=116 time=36.9 ms
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=6 ttl=116 time=44.2 ms
64 bytes from lctsaa-ac-in-x0e.1e100.net (2404:6800:4012:9::200e): icmp_seq=7 ttl=116 time=15.0 ms
^C
--- google.com ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 6009ms
rtt min/avg/max/mdev = 14.987/32.779/44.184/10.431 ms

About Kenel config for IPv6

CONFIG_IPV6=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y # CONFIG_IPV6_OPTIMISTIC_DAD is not set CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_ESP_OFFLOAD=m CONFIG_INET6_ESPINTCP=y CONFIG_INET6_IPCOMP=m CONFIG_IPV6_MIP6=m CONFIG_IPV6_ILA=m CONFIG_INET6_XFRM_TUNNEL=m CONFIG_INET6_TUNNEL=m CONFIG_IPV6_VTI=m CONFIG_IPV6_SIT=m CONFIG_IPV6_SIT_6RD=y CONFIG_IPV6_NDISC_NODETYPE=y CONFIG_IPV6_TUNNEL=m CONFIG_IPV6_GRE=m CONFIG_IPV6_FOU=m CONFIG_IPV6_FOU_TUNNEL=m CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_IPV6_MROUTE=y CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y CONFIG_IPV6_PIMSM_V2=y CONFIG_IPV6_SEG6_LWTUNNEL=y CONFIG_IPV6_SEG6_HMAC=y CONFIG_IPV6_SEG6_BPF=y # CONFIG_IPV6_RPL_LWTUNNEL is not set


ref: Gemini

2026年1月6日 星期二

find duplicates IMEI


find_duplicates.py by Gemini

import sys

def process_imeis(input_filename):
    seen = set()
    duplicates = set()
    output_filename = "unique_results.txt"
    
    try:
        # Step 1: Read and identify duplicates
        with open(input_filename, 'r') as file:
            for line in file:
                imei = line.strip()
                if not imei:
                    continue
                
                if imei in seen:
                    duplicates.add(imei)
                else:
                    seen.add(imei)

        # Step 2: Save Unique IMEIs to file
        with open(output_filename, 'w') as out_file:
            for imei in sorted(seen):
                out_file.write(f"{imei}\n")

        # Step 3: Print Statistics and List Duplicates
        print(f"\n{'='*40}")
        print(f"FILE PROCESSED: {input_filename}")
        print(f"{'='*40}")
        print(f"Unique Count:    {len(seen)}")
        print(f"Duplicate Count: {len(duplicates)}")
        print(f"Unique list saved to: {output_filename}")
        print(f"{'='*40}\n")

        if duplicates:
            print("LIST OF DUPLICATE IMEIs:")
            print("-" * 25)
            # Sorting the list makes it easier to read
            for imei in sorted(duplicates):
                print(imei)
        else:
            print("No duplicate numbers were found.")

    except FileNotFoundError:
        print(f"Error: Could not find the file '{input_filename}'.")
    except PermissionError:
        print(f"Error: Permission denied when accessing files.")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 check_imei.py <filename.txt>")
    else:
        process_imeis(sys.argv[1])

2025年12月19日 星期五

FN920C04 auto-connected on Nvidia AGX Orin R36.3

Data structure
$ tree -L 1
.
├── apply_binaries.sh
├── bootloader
├── build_l4t_bup.sh
├── flash.sh
├── generate_capsule
├── ...
├── ...
├── kernel
├── l4t_generate_soc_bup.sh
├── l4t_sign_image.sh
├── l4t_uefi_sign_image.sh
├── ...
├── ...
├── rootfs
├── source
├── Tegra_Software_License_Agreement-Tegra-Linux.txt
└── tools

Copy the data to the rootfs, /etc/NetworkManager/system-connections/internet.nmconnection
[connection]

id=internet

uuid=996797c1-56eb-44aa-b7e2-d405ade10bdf

type=gsm

[gsm]

apn=internet

[serial]

[ppp]

[ipv4]

dns-priority=120

method=auto

route-metric=1050

[ipv6]

addr-gen-mode=stable-privacy

method=auto

[proxy]

Change root.


$ ls -alh /run/dbus/
srw-rw-rw-  1 root root    0 Dec 19 09:53 system_bus_socket

$ sudo mount -t proc /proc ./rootfs/proc
$ sudo mount -t sysfs /sys ./rootfs/sys
$ sudo mount -o bind /dev ./rootfs/dev
$ sudo mount -o bind /dev/pts ./rootfs/dev/pts
// for dbus
$ mkdir ./rootfs/run/dbus
$ sudo mount -o bind /run/dbus ./rootfs/run/dbus      //PC env: ubuntu 22.04, kernel: 6.8.0-65-generic 

$ sudo chroot ./rootfs

//enter simulator 
#nmcli connection reload

Rebuild BSP code
r363

FN920C04 should be auto-connected to the Internet
$ ifconfig wwan0
wwan0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
        inet 10.231.60.110  netmask 255.255.255.252  destination 10.231.60.110
        inet6 2001:b400:e2ad:ab1:95f4:6e35:7e1a:4a5e  prefixlen 64  scopeid 0x0<global>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 379  bytes 67264 (67.2 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 453  bytes 39083 (39.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        

$ mmcli -m 0
  ----------------------------------
  General  |                   path: /org/freedesktop/ModemManager1/Modem/0
  ----------------------------------
  Hardware |           manufacturer: Telit Cinterion
           |                  model: FN920C04-WW
           |      firmware revision: M0V.060001
           |         carrier config: default
           |           h/w revision: 1.10
  ----------------------------------
  System   |                 device: /sys/devices/platform/bus@0/3610000.usb/usb1/1-4/1-4.2
           |                drivers: qmi_wwan, option
           |                 plugin: telit
           |           primary port: cdc-wdm0
           |                  ports: cdc-wdm0 (qmi), ttyUSB0 (ignored), ttyUSB1 (at),
           |                         ttyUSB2 (ignored), wwan0 (net)
  ----------------------------------
  ----------------------------------
  Status   |                   lock: sim-pin2
           |         unlock retries: sim-pin (3), sim-puk (10), sim-pin2 (3), sim-puk2 (10)
           |                  state: connected
           |            power state: on
           |            access tech: lte
           |         signal quality: 100% (recent)
  ----------------------------------
  ----------------------------------
  3GPP EPS |   ue mode of operation: csps-2
           |    initial bearer path: /org/freedesktop/ModemManager1/Bearer/0
           | initial bearer ip type: ipv4v6
  ----------------------------------
  SIM      |       primary sim path: /org/freedesktop/ModemManager1/SIM/0
           |         sim slot paths: slot 1: /org/freedesktop/ModemManager1/SIM/0 (active)
           |                         slot 2: none
  ----------------------------------
  Bearer   |                  paths: /org/freedesktop/ModemManager1/Bearer/1



$ mmcli -b 1
  ------------------------------------
  General            |           path: /org/freedesktop/ModemManager1/Bearer/1
                     |           type: default
  ------------------------------------
  Status             |      connected: yes
                     |      suspended: no
                     |    multiplexed: no
                     |      interface: wwan0
                     |     ip timeout: 20
  ------------------------------------
  Properties         |            apn: internet
                     |        roaming: allowed
                     |        ip type: ipv4v6
                     |   allowed-auth: none, pap, chap, mschap, mschapv2, eap
  ------------------------------------
  IPv4 configuration |         method: static
                     |        address: 10.231.60.110
                     |         prefix: 30
                     |        gateway: 10.231.60.109
                     |            dns: 168.95.1.1, 168.95.192.1
                     |            mtu: 1500
  ------------------------------------
  IPv6 configuration |         method: static
                     |        address: 2001:b400:e2ad:ab1:95f4:6e35:7e1a:4a5e
                     |         prefix: 64
                     |        gateway: 2001:b400:e2ad:ab1:f9db:c24f:25cc:9eb2
                     |            dns: 2001:b000:168::1, 2001:b000:168::2
                     |            mtu: 1500
  ------------------------------------
  Statistics         |     start date: 2025-12-19T07:08:00Z
                     |       duration: 7290
                     |       bytes rx: 71708
                     |       bytes tx: 40369
                     |       attempts: 1
                     | total-duration: 7290
                     | total-bytes rx: 71708
                     | total-bytes tx: 40369

2025年12月18日 星期四

System ID for GNSS L1+L5

NMEA 0183 Version 4.10 (or higher)


Simulators, ex:
  1. Spirent gss 7000
  2. GSP6 Series

GPS
L1 Data (System ID = 1):
$GPGSV,3,1,09,10,30,150,45,12,40,050,44,15,20,310,40,18,60,120,48,1*6A

L5Q Data (System ID = 8):
$GPGSV,2,1,05,10,30,150,38,12,40,050,35,18,60,120,42,,,,8*6F

BeiDou
B1C
$BDGSV,2,1,07,21,30,186,48,24,24,260,37,26,19,211,35,34,13,302,28,3*76


B1i
$BDGSV,7,3,26,08,58,213,36,04,39,116,29,03,58,204,35,01,51,135,,1*7A


$BDGSV,2,1,07,19,28,048,17,21,30,186,39,26,19,211,26,34,13,302,21,5*7


ref:
GPGSV
System ID

2025年12月2日 星期二

Compile ModemManager

Manual compilation

Install depends on the packages

$ sudo apt install meson ninja-build python3-jinja2 libudev-dev libpolkit-gobject-1-dev libmbim-glib-dev libqrtr-glib-dev xsltproc help2man gettext gperf libcap-dev libgirepository1.0-dev libdbus-1-dev

ARM64
$ sudo apt install meson ninja-build python3-jinja2 libudev-dev libpolkit-gobject-1-dev libmbim-glib-dev xsltproc help2man gettext gperf libcap-dev\
libgirepository1.0-dev libdbus-1-dev bash-completion libgudev-1.0-dev libudev-dev libsystemd-dev

elogind

$ pip3 install jinja2
$ git clone https://github.com/elogind/elogind.git
$ cd elogind
$ meson setup build
$ ninja -C build
$ cd build; ninja install

libqrtr
$ git clone https://gitlab.freedesktop.org/mobile-broadband/libqrtr-glib.git
$ cd libqrtr-glib
$ meson setup builddir --prefix=/usr -Dintrospection=true //for all project
$ ninja -C build
$ cd build; ninja install

$ cp /usr/local/share/gir-1.0/Qrtr-1.0.gir /usr/share/gir-1.0/
libqmi
$ git clone https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
$ cd libqmi
$ meson setup build
$ ninja -C build

if encounter the issue...go back previous step, libqrtr
..
..
Couldn't find include 'Qrtr-1.0.gir' (search path: '['/usr/share/gir-1.0', '/usr/share', 'gir-1.0', '/usr/share/gir-1.0', '/usr/share/gir-1.0']')
..
..


ModemManager

$ git clone https://github.com/linux-mobile-broadband/ModemManager
$ cd ModemManager
$ meson setup build_x86 --prefix=/usr --sysconfdir=/etc
$ cd build_x86
$ ninja


RSSI

...
...
ModemManager[27171]: <debug> [1736232436.167606] [modem0] lte_rssi: -48
ModemManager[27171]: <debug> [1736232436.167632] [modem0] RSSI (LTE): -48 dBm
ModemManager[27171]: <debug> [1736232436.167647] [modem0] RSSI: -48 dBm --> 100%
ModemManager[27171]: <debug> [1736232436.167749] [modem0] signal quality updated (100)
ModemManager[27171]: <debug> [1736232436.167771] [modem0] periodic signal quality and access technology checks scheduled
ModemManager[27171]: <debug> [1736232436.199497] [/dev/cdc-wdm1] received message...
......



elogind Troubleshoot:
only show on Ubuntu 20.04, not 22.04
In file included from ../src/basic/socket-util.c:16:
../src/basic/linux/if.h:216:3: error: unknown type name ‘x25_hdlc_proto’
  216 |   x25_hdlc_proto  *x25;
      |   ^~~~~~~~~~~~~~
[172/543] Compiling C object src/basic/libbasic.a.p/terminal-util.c.o
ninja: build stopped: subcommand failed.


src/basic/linux/if.h
...
...
#include <linux/hdlc/ioctl.h>
...
...
struct if_settings {
        unsigned int type; /* Type of physical device or protocol */
        unsigned int size; /* Size of the data allocated by the caller */
        union {
                /* {atm/eth/dsl}_settings anyone ? */
                raw_hdlc_proto *raw_hdlc;
                cisco_proto *cisco;
                fr_proto *fr;
                fr_proto_pvc *fr_pvc;
                fr_proto_pvc_info *fr_pvc_info;
                x25_hdlc_proto		*x25;

                /* interface settings */
                sync_serial_settings *sync;
                te1_settings *te1;
        } ifs_ifsu;
};

only ubuntu 22.04 has it, not 20.04
/usr/include/linux/hdlc/ioctl.h
typedef struct {
      unsigned short dce; /* 1 for DCE (network side) operation */
      unsigned int modulo; /* modulo (8 = basic / 128 = extended) */
      unsigned int window; /* frame window size */
      unsigned int t1; /* timeout t1 */
      unsigned int t2; /* timeout t2 */
      unsigned int n2; /* frame retry counter */
  } x25_hdlc_proto;




2025年9月18日 星期四

QMDL to Pcap


scat
https://github.com/fgsect/scat

Install
Install the necessary package via pip:
$ pip3 install pyusb pyserial bitstring packaging


$ cd src/
$ pip3 install -e .
$ python3 -m scat.main --help

Usage
$ scat --version
$ scat --help

$ scat -t qc -d 250812-174032_250812-174109.qmdl -F output.pcap

2025年7月24日 星期四

Enable GNSS_NMEA for FN920C04/FN990A28 PCIE

based on:fn920c04-pcie-on-x86

vim drivers/bus/mhi/host/pci_generic.c
static const struct mhi_channel_config mhi_telit_fn920c04_channels[] = {
        MHI_CHANNEL_CONFIG_UL(0, "GNSS", 32, 1),
        MHI_CHANNEL_CONFIG_DL(1, "GNSS", 32, 1),
        MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
        MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
        MHI_CHANNEL_CONFIG_UL(4, "DIAG", 64, 1),
        MHI_CHANNEL_CONFIG_DL(5, "DIAG", 64, 1),
        MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0),
        MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0),
        
        MHI_CHANNEL_CONFIG_UL(30, "DUN3", 32, 0),
        MHI_CHANNEL_CONFIG_DL(31, "DUN3", 32, 0),
        
        MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
        MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
        MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
        MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
        MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1),
        MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1),
        
        MHI_CHANNEL_CONFIG_UL(94, "DUN4", 32, 1),
        MHI_CHANNEL_CONFIG_DL(95, "DUN4", 32, 1),
        
        MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2),
        MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3),
};


vim drivers/net/wwan/mhi_wwan_ctrl.c
static const struct mhi_device_id mhi_wwan_ctrl_match_table[] = {
        { .chan = "DUN", .driver_data = WWAN_PORT_AT },
        { .chan = "DUN2", .driver_data = WWAN_PORT_AT },
        
        { .chan = "DUN3", .driver_data = WWAN_PORT_AT },
        { .chan = "DUN4", .driver_data = WWAN_PORT_AT },
        
        { .chan = "MBIM", .driver_data = WWAN_PORT_MBIM },
        { .chan = "QMI", .driver_data = WWAN_PORT_QMI },
        { .chan = "DIAG", .driver_data = WWAN_PORT_QCDM },
        { .chan = "FIREHOSE", .driver_data = WWAN_PORT_FIREHOSE },
        {},
};
MODULE_DEVICE_TABLE(mhi, mhi_wwan_ctrl_match_table);

plugins/telit/mm-plugin-telit.c (not test)
mm_plugin_create (void)
{
    static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL };
    /* Vendors: Telit */
    static const guint16 vendor_ids[] = { 0x1bc7, 0 };
    static const mm_uint16_pair subsystem_vendor_ids[] = {
        { 0x17cb, 0x1c5d }, /* FN990 */ //for PCIE, chatgpt said. But FN990A28 PCIE: 0x17cb, 0x0308 via lspci
        { 0, 0 }
    };
    ...
    ...
}

minicom -D /dev/wwan0at1
AT$GPSNMUN=1,1,1,1,1,1,1
AT$GPSP=1

minicom -D /dev/wwan0at3
$GPSNMUN: $GPGSA,A,1,,,,,,,,,,,,,,,,*32
$GPSNMUN: $GPVTG,,T,,M,,N,,K,N*2C
$GPSNMUN: $GPRMC,,V,,,,,,,,,,N,V*29
$GPSNMUN: $GPGGA,,,,,,0,,,,,,,,*66
$GPSNMUN: $GPGLL,,,,,,V,N*64
...
...

FN990
static const struct mhi_channel_config mhi_telit_fn990_channels[] = {
        MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
        MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
        MHI_CHANNEL_CONFIG_UL(4, "DIAG", 64, 1),
        MHI_CHANNEL_CONFIG_DL(5, "DIAG", 64, 1),
        MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
        MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0),
        MHI_CHANNEL_CONFIG_UL(30, "DUN3", 32, 0),
        MHI_CHANNEL_CONFIG_DL(31, "DUN3", 32, 0),
        MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
        MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
        MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1),
        MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1),
        MHI_CHANNEL_CONFIG_UL(94, "DUN4", 32, 1),
        MHI_CHANNEL_CONFIG_DL(95, "DUN4", 32, 1),
        MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2),
        MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3),
};

2025年7月1日 星期二

FN920C04 PCIE on X86_64

Kernel: 6.9.12
  • Modify Kernel Code
    a. pci_generic.c
    b. kernel config
    $ grep -i "mhi" ./config-6.9.12
    CONFIG_QRTR_MHI=m
    # CONFIG_PCI_EPF_MHI is not set
    CONFIG_MHI_BUS=m
    CONFIG_MHI_BUS_DEBUG=y
    CONFIG_MHI_BUS_PCI_GENERIC=m
    CONFIG_MHI_BUS_EP=m
    CONFIG_MHI_NET=m
    CONFIG_MHI_WWAN_CTRL=m
    CONFIG_MHI_WWAN_MBIM=m
    
    c. lspci
    $ lspci -nn
    03:00.0 Unassigned class [ff00]: Qualcomm Device [17cb:011a]
    
    $ lspci -k
    03:00.0 Unassigned class [ff00]: Qualcomm Device 011a
            Subsystem: Device 1c5d:2020
            Kernel driver in use: mhi-pci-generic
            Kernel modules: mhi_pci_generic
    
    d. dmesg
    $ sudo dmesg | grep -i mhi
    [    5.721867] mhi_pci_generic: module verification failed: signature and/or required key missing - tainting kernel
    [    5.724379] mhi-pci-generic 0000:03:00.0: MHI PCI device found: telit-fn920c04
    [    5.724387] mhi-pci-generic 0000:03:00.0: BAR 0 [mem 0xd0801000-0xd0801fff 64bit]: assigned
    [    5.729509] mhi mhi0: Requested to power ON
    [    5.729535] mhi mhi0: Power on setup success
    
    e. device node
    $ ls -alh /dev/wwan0*
    crw------- 1 root root 242, 2  七   2 09:37 /dev/wwan0at0
    crw------- 1 root root 242, 3  七   2 09:37 /dev/wwan0at1
    crw------- 1 root root 242, 0  七   2 09:37 /dev/wwan0qcdm0
    crw------- 1 root root 242, 1  七   2 09:37 /dev/wwan0qmi0
    
    f. interface
    $ ls /sys/class/net/
    eno1  lo  mhi_hwip0
    
    
    $ udevadm info /sys/class/net/mhi_hwip0
    P: /devices/pci0000:00/0000:00:1c.0/0000:03:00.0/mhi0/mhi0_IP_HW0/net/mhi_hwip0
    L: 0
    E: DEVPATH=/devices/pci0000:00/0000:00:1c.0/0000:03:00.0/mhi0/mhi0_IP_HW0/net/mhi_hwip0
    E: INTERFACE=mhi_hwip0
    E: IFINDEX=3
    E: SUBSYSTEM=net
    E: USEC_INITIALIZED=5963171
    E: ID_MM_CANDIDATE=1
    E: ID_BUS=pci
    E: ID_VENDOR_ID=0x17cb
    E: ID_MODEL_ID=0x011a
    E: ID_PCI_CLASS_FROM_DATABASE=Unassigned class
    E: ID_VENDOR_FROM_DATABASE=Qualcomm
    E: ID_PATH=pci-0000:03:00.0
    E: ID_PATH_TAG=pci-0000_03_00_0
    E: ID_NET_DRIVER=mhi_net
    E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
    E: ID_NET_NAME=mhi_hwip0
    E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/mhi_hwip0
    E: TAGS=:systemd:
    E: CURRENT_TAGS=:systemd:
    
FN920C04 FW version (要B022, B015有問題)
at#swpkgv
49.00.000-B022-P0V.130000
M0V.060000-B022
P0V.130000
A0V.000000-B022

qmicli+udhcpc, 但系統無法取得IP, 用ip route解決

#write profile in /etc/qmi-network.conf
    APN=INTERNET #CHT
    APN_USER=
    APN_PASS=
    PROXY=yes

$ qmicli -V
qmicli 1.32.0

$ sudo qmicli -d /dev/wwan0qmi0 --dms-set-operating-mode='online'
[/dev/wwan0qmi0] Operating mode set successfully


$ sudo ip link set wwan0 up


$ sudo qmicli -d /dev/wwan0qmi0 --wda-get-data-format
[/dev/wwan0qmi0] Successfully got data format
                   QoS flow header: no
               Link layer protocol: 'raw-ip'
  Uplink data aggregation protocol: 'disabled'
Downlink data aggregation protocol: 'disabled'
                     NDP signature: '0'
Downlink data aggregation max datagrams: '0'
Downlink data aggregation max size: '0'


$ sudo qmi-network /dev/wwan0qmi0 start
Loading profile at /etc/qmi-network.conf...
    APN: INTERNET
    APN user: unset
    APN password: unset
    qmi-proxy: yes
    IP_TYPE: unset
    PROFILE: unset
Checking data format with 'qmicli -d /dev/wwan0qmi0 --wda-get-data-format --device-open-proxy'...
Device link layer protocol retrieved: raw-ip
Getting expected data format with 'qmicli -d /dev/wwan0qmi0 --get-expected-data-format'...
error: cannot get expected data format: Setting expected data format management is unsupported by the driver
Expected link layer protocol not retrieved: kernel unsupported
Updating device link layer protocol with 'qmicli -d /dev/wwan0qmi0 --wda-set-data-format=802-3 --device-open-proxy'...
New device link layer protocol retrieved: raw-ip
Starting network with 'qmicli -d /dev/wwan0qmi0 --wds-start-network=apn='INTERNET'  --client-no-release-cid --device-open-proxy'...
Saving state at /tmp/qmi-network-state-wwan0qmi0... (CID: 15)
Saving state at /tmp/qmi-network-state-wwan0qmi0... (PDH: 3771166512)
Network started successfully


$ sudo qmicli -d /dev/wwan0qmi0 --wds-get-current-settings
[/dev/wwan0qmi0] Current settings retrieved:
           IP Family: IPv4
        IPv4 address: 10.169.211.193
    IPv4 subnet mask: 255.255.255.252
IPv4 gateway address: 10.169.211.194
    IPv4 primary DNS: 168.95.1.1
  IPv4 secondary DNS: 168.95.192.1
                 MTU: 1500
             Domains: none


$ sudo ip addr add 10.169.211.193/30 dev mhi_hwip0


$ ip route
10.169.211.192/30 dev mhi_hwip0 proto kernel scope link src 10.169.211.193



$ sudo ip route add default via 10.169.211.193


$ ifconfig mhi_hwip0
mhi_hwip0: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 16384
        inet 10.169.211.193  netmask 255.255.255.252  destination 10.169.211.193
        inet6 fe80::200:ff:fe00:0  prefixlen 64  scopeid 0x20<link>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6  bytes 288 (288.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

$ ping -I mhi_hwip0 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 10.169.211.193 mhi_hwip0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=432 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=114 time=35.6 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=114 time=32.7 ms
...
...

$ ping google.com
ping: google.com: Temporary failure in name resolution



$ echo "nameserver 168.95.1.1" | sudo tee /etc/resolv.conf
nameserver 168.95.1.1


$ ping google.com
PING google.com (142.250.198.78) 56(84) bytes of data.
64 bytes from lctsaa-ab-in-f14.1e100.net (142.250.198.78): icmp_seq=1 ttl=114 time=15.5 ms
64 bytes from lctsaa-ab-in-f14.1e100.net (142.250.198.78): icmp_seq=2 ttl=114 time=40.8 ms
64 bytes from lctsaa-ab-in-f14.1e100.net (142.250.198.78): icmp_seq=3 ttl=114 time=39.9 ms
...
...

ModemManager (FN920C04 PCIE是用 wwan0qmi0)
$ mmcli --version
mmcli 1.18.6
Copyright (2011 - 2021) 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.

$ mmcli -L
    /org/freedesktop/ModemManager1/Modem/0 [Telit Cinterion] FN920C04-WW

$ mmcli -m 0
  ----------------------------------
  General  |                   path: /org/freedesktop/ModemManager1/Modem/0
           |              device id: 6be85a5a4dfee78138924ac7c4fa075a8ebca546
  ----------------------------------
  Hardware |           manufacturer: Telit Cinterion
           |                  model: FN920C04-WW
           |      firmware revision: M0V.060000-B022
           |         carrier config: default
           |           h/w revision: 1.20
           |              supported: lte, 5gnr
           |                current: lte, 5gnr
           |           equipment id: 355411761011444
  ----------------------------------
  System   |                 device: /sys/devices/pci0000:00/0000:00:1c.0/0000:03:00.0
           |                drivers: mhi_net, mhi-pci-generic
           |                 plugin: generic
           |           primary port: wwan0qmi0
           |                  ports: mhi_hwip0 (net), wwan0at0 (at), wwan0at1 (at),
           |                         wwan0qmi0 (qmi)
  ----------------------------------
...
...
...
  ----------------------------------
  3GPP EPS |   ue mode of operation: csps-2
           |    initial bearer path: /org/freedesktop/ModemManager1/Bearer/0
           | initial bearer ip type: ipv4
  ----------------------------------
  SIM      |       primary sim path: /org/freedesktop/ModemManager1/SIM/0
           |         sim slot paths: slot 1: /org/freedesktop/ModemManager1/SIM/0 (active)
           |                         slot 2: none


troubleshooting
1. disable ModemManager and poweroff.
$ sudo qmicli -d /dev/wwan0qmi0 --dms-set-operating-mode='online'
[sudo] password for ubuntu:
[02  七  2025, 10:03:49] -Warning ** Error reading from istream: Resource temporarily unavailable
error: couldn't create client for the 'dms' service: CID allocation failed in the CTL client: endpoint hangup

2. 目前的FN920C04 FW(B22), 好像還不能使用ModemManager...升級1.24後再來試試
$ sudo mmcli -m 0 --create-bearer='apn=internet,ip-type=ipv4'
[sudo] password for ubuntu:
Successfully created new bearer in modem:
        /org/freedesktop/ModemManager1/Bearer/1

$ sudo mmcli -m 0 --simple-connect='apn=internet,ip-type=ipv4v6'
error: couldn't connect the modem: 'GDBus.Error:org.freedesktop.ModemManager1.Error.MobileEquipment.NetworkTimeout: Network timeout'

2025年5月22日 星期四

Send SMS via AT commands

Module:
Telit LN920A06
def text_to_ucs2_hex(text):
    return text.encode('utf-16-be').hex().upper()

def encode_phone_number(number):
    # 處理台灣號碼,如 0966123456 → +886966123456
    if number.startswith('0'):
        number = '886' + number[1:]
    elif number.startswith('+'):
        number = number[1:]

    if len(number) % 2 != 0:
        number += 'F'

    swapped = ''.join([number[i+1] + number[i] for i in range(0, len(number), 2)])
    return swapped, len(number)

def generate_pdu_and_cmgs(phone, message):
    # UCS2 編碼
    ucs2 = text_to_ucs2_hex(message)
    ucs2_len = len(ucs2) // 2  # bytes

    # 編碼電話號碼
    phone_enc, phone_digits = encode_phone_number(phone)
    phone_len_hex = f"{phone_digits:X}".zfill(2)

    # 組合 PDU
    smsc = "00"
    pdu_type = "11"
    mr = "00"
    toa = "91"
    pid = "00"
    dcs = "08"
    vp = "AA"  # 約 4 天
    udl = f"{ucs2_len:02X}"

    pdu_body = (
        pdu_type + mr + phone_len_hex + toa + phone_enc +
        pid + dcs + vp + udl + ucs2
    )

    full_pdu = smsc + pdu_body

    # CMGS = 從 PDU_TYPE 開始的長度(即去掉 SMSC 的 "00" 開頭,長度為 1 byte = 2 hex)
    cmgs_len = (len(full_pdu) - 2) // 2

    return full_pdu, cmgs_len

# 測試
phone = "0911223333"
message = "哈囉世界"

pdu, cmgs_len = generate_pdu_and_cmgs(phone, message)
print(f"AT+CMGF=0")
print(f"AT+CMGS={cmgs_len}")
print(pdu)


Send the message by AT command
AT+CMGF=0 //PDU
OK

AT+CMGS=22
0011000C918896553679610008AA0854C856C94E16754C(ctrl+z)
+CMGS: 18

OK

ref:
1. GSM Modem 傳送簡訊的幾個 AT 指令
2. GPRS應用之: AT指令發送PDU簡訊詳解

2025年5月20日 星期二

enable gnss_nmea for Le910Cx

Le910Cx開啟gnss nmea時,
用哪個ttyUSB開啟,就會直接輸出,加入下面條件,應該可以開機時,順利啟動NMEA
環境: NV Orin R36.3

avoid blocking ttyUSB2 by modemmanager
$ cat /etc/udev/rules.d/99-le910Cx.rules
KERNEL=="ttyUSB2", SUBSYSTEM=="tty", ENV{ID_VENDOR_ID}=="1bc7", ENV{ID_MODEL_ID}=="1031", ENV{ID_MM_DEVICE_IGNORE}="1"  or ENV{ID_MM_GNSS_PORT}="1"

。Setting ENV{ID_MM_DEVICE_IGNORE}="1" on a USB device or interface tells ModemManager to ignore all related ports.
。ENV{ID_MM_DEVICE_IGNORE}="1" tells ModemManager to ignore this port only.


enable_gnss.c (delay 5 secs)
#include <errno.h>
#include <fcntl.h>
#include <libudev.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>

#define TIMEOUT_MS 3000 // Timeout in milliseconds
#define MAX_BUFFER_SIZE 256
#define MAX_AT_RESPONSE 16
#define MAX_DEVICE_PATH 64 // Define a constant for device path size

// Structure to hold AT command and its response time
typedef struct {
  char command[32];
  double response_time;
} ATCommand;

// Structure to hold the AT command responses
typedef struct {
  char device_path[MAX_DEVICE_PATH]; // Use the defined constant
  char response[MAX_BUFFER_SIZE];
} DeviceResponse;

DeviceResponse device_responses[MAX_AT_RESPONSE];

pthread_mutex_t response_mutex = PTHREAD_MUTEX_INITIALIZER;
int response_count = 0;

// Function prototypes (for clarity)
int open_serial_port(const char *port_name);
int send_at_command(int fd, const char *command, char *response,
                    size_t response_size, double response_time);
void close_serial_port(int fd);
void *process_device(void *arg);
int find_tty_acm(int argc, char *argv[], int *get_count,
                 char dev_path[][MAX_DEVICE_PATH]); // Use the defined constant
void GetResponseData(char *input, char *output, size_t array_size,
                     int line_sopt);

ATCommand ATCS[] = {
    {"at$GPSP=1", 0.5}, // Example response time of 300ms (converted to seconds)
    {"AT$GPSNMUN=1,1,1,1,1,1,1", 0.4}, // Example response time of 400ms
};

int open_serial_port(const char *port_name) {
  int fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1) {
    perror("open_serial_port: open failed");
    return -1;
  }

  struct termios tty;
  memset(&tty, 0, sizeof(tty));
  if (tcgetattr(fd, &tty) != 0) {
    perror("open_serial_port: tcgetattr failed");
    close(fd);
    return -1;
  }

  cfsetospeed(&tty, B115200); // Set baud rate (adjust as needed)
  cfsetispeed(&tty, B115200);

  tty.c_cflag &= ~PARENB; // No parity
  tty.c_cflag &= ~CSTOPB; // 1 stop bit
  tty.c_cflag &= ~CSIZE;
  tty.c_cflag |= CS8;            // 8 data bits
  tty.c_cflag &= ~CRTSCTS;       // No hardware flow control
  tty.c_cflag |= CREAD | CLOCAL; // Enable read and ignore control lines

  tty.c_lflag &= ~ICANON; // Disable canonical mode
  tty.c_lflag &= ~ECHO;   // Disable echo
  tty.c_lflag &= ~ECHOE;  // Disable erasure
  tty.c_lflag &= ~ECHONL; // Disable new-line echo
  tty.c_lflag &= ~ISIG;   // Disable interpretation of interrupt, quit, and
                          // suspend characters

  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
  tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
                   ICRNL); // Disable special handling of received bytes

  tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes
  tty.c_oflag &=
      ~ONLCR; // Prevent conversion of newline to carriage return/line feed

  tty.c_cc[VTIME] = TIMEOUT_MS / 100; // Timeout in deciseconds (correct)
  tty.c_cc[VMIN] = 0;                 // Minimum number of characters to receive

  if (tcsetattr(fd, TCSANOW, &tty) != 0) {
    perror("open_serial_port: tcsetattr failed");
    close(fd);
    return -1;
  }

  return fd;
}

int send_at_command(int fd, const char *command, char *response,
                    size_t response_size, double response_time) {
  tcflush(fd, TCIFLUSH); // Flush the input buffer

  ssize_t bytes_written = write(fd, command, strlen(command));
  if (bytes_written < 0) {
    perror("send_at_command: write failed");
    return -1;
  }

  memset(response, 0, response_size);
  size_t total_bytes_read = 0;

  struct timeval start_time, current_time;
  gettimeofday(&start_time, NULL); // Use gettimeofday for better precision

  while (total_bytes_read < response_size - 1) {
    ssize_t bytes_read = read(fd, response + total_bytes_read,
                              response_size - 1 - total_bytes_read);
    if (bytes_read < 0) {
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
        gettimeofday(&current_time, NULL);
        double elapsed_time =
            (current_time.tv_sec - start_time.tv_sec) +
            (current_time.tv_usec - start_time.tv_usec) / 1000000.0;

        if (elapsed_time > response_time) {
          tcflush(fd, TCIFLUSH); // clear the buffer
          fprintf(stderr, "send_at_command: Timeout occurred\n");
          return -1; // Timeout occurred
        }
        usleep(10000); // small delay before retry (10ms)
        continue;      // retry read
      } else {
        perror("send_at_command: read failed");
        return -1;
      }
    }

    if (bytes_read == 0) {
      // End of file or no more data
      break;
    }

    total_bytes_read += bytes_read;
    if (strstr(response, "OK\r\n") != NULL) {
      break; // Stop reading when OK is received.
    }
  }

  response[total_bytes_read] = '\0'; // Null-terminate the string
  return 0;
}

void close_serial_port(int fd) { close(fd); }

void *process_device(void *arg) {
  int serial_fd;
  char *dev_path = (char *)arg;
  char response[MAX_BUFFER_SIZE];
  int at_command_count =
      sizeof(ATCS) / sizeof(ATCS[0]); // Calculate number of AT commands

  printf("Thread processing device: %s\n", dev_path);
  serial_fd = open_serial_port(dev_path);
  if (serial_fd == -1) {
    fprintf(stderr, "Error opening serial port: %s\n", strerror(errno));
    return NULL;
  }

  // Send AT commands
  for (int i = 0; i < at_command_count; i++) {
    char command[128]; // Ensure enough space for the command
    snprintf(command, sizeof(command) - 1, "%s\r",
             ATCS[i].command); // Append \r

    if (send_at_command(serial_fd, command, response, sizeof(response),
                        ATCS[i].response_time) == 0) {
      printf("Response to '%s': %s\n", ATCS[i].command, response);

      pthread_mutex_lock(&response_mutex);
      if (response_count < MAX_AT_RESPONSE) {
        strncpy(device_responses[response_count].device_path, dev_path,
                MAX_DEVICE_PATH - 1);
        device_responses[response_count].device_path[MAX_DEVICE_PATH - 1] =
            '\0'; // Ensure null termination

        strncpy(device_responses[response_count].response, response,
                MAX_BUFFER_SIZE - 1);
        device_responses[response_count].response[MAX_BUFFER_SIZE - 1] =
            '\0'; // Ensure null termination

        response_count++; // Increment response count

      } else {
        fprintf(stderr, "Response buffer is full!\n");
      }
      pthread_mutex_unlock(&response_mutex);

    } else {
      fprintf(stderr, "Error sending command '%s'\n", ATCS[i].command);
    }
  }

  close_serial_port(serial_fd);
  pthread_exit(NULL);
}

int find_tty_acm(int argc, char *argv[], int *get_count,
                 char dev_path[][MAX_DEVICE_PATH]) {
  if (argc != 4) {
    fprintf(stderr, "Usage: %s  \n", argv[0]); // Corrected Usage message
    return 1;
  }

  const char *target_vid = argv[1];
  const char *target_pid = argv[2];
  const char *target_bInterfaceNumber = argv[3];

  struct udev *udev = udev_new();
  if (!udev) {
    fprintf(stderr, "Failed to create udev object\n");
    return 1;
  }

  struct udev_enumerate *enumerate = udev_enumerate_new(udev);
  udev_enumerate_add_match_subsystem(enumerate, "tty");
  udev_enumerate_add_match_property(enumerate, "ID_VENDOR_ID", target_vid);
  udev_enumerate_add_match_property(enumerate, "ID_MODEL_ID", target_pid);
  udev_enumerate_scan_devices(enumerate);

  struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
  struct udev_list_entry *entry;

  int i = 0;
  udev_list_entry_foreach(entry, devices) {
    const char *path = udev_list_entry_get_name(entry);
    struct udev_device *dev = udev_device_new_from_syspath(udev, path);
    if (!dev) {
      fprintf(stderr, "Error creating udev_device from syspath: %s\n", path);
      continue; // Skip to the next device
    }

    const char *devnode = udev_device_get_devnode(dev);
    const char *bInterface =
        udev_device_get_property_value(dev, "ID_USB_INTERFACE_NUM");

    if (devnode && bInterface &&
        strcmp(bInterface, target_bInterfaceNumber) == 0) {
      if (i < MAX_AT_RESPONSE) { // Check for overflow
        strncpy(dev_path[i], devnode, MAX_DEVICE_PATH - 1); // Use strncpy
        dev_path[i][MAX_DEVICE_PATH - 1] = '\0';            // Null-terminate
        printf("Found device: %s\n", devnode);
        i++;
        (*get_count)++;
      } else {
        fprintf(stderr, "Too many devices found.  Increase MAX_AT_RESPONSE.\n");
        break;
      }
    }
    udev_device_unref(dev);
  }

  udev_enumerate_unref(enumerate); // Free enumerate object
  udev_unref(udev);

  return 0;
}

void GetResponseData(char *input, char *output, size_t array_size,
                     int line_sopt) {
  char *line, *saveptr;
  int stop = 0;

  output[0] = '\0'; // Initialize output string

  line = strtok_r(input, "\n", &saveptr);
  while (line != NULL) {
    if (stop == line_sopt) {
      strncpy(output, line, array_size - 1); // Use strncpy for safety
      output[array_size - 1] = '\0';         // Ensure null termination
      return;
    }
    line = strtok_r(NULL, "\n", &saveptr);
    stop++;
  }
  output[0] = '\0'; // If line_sopt is out of range, return an empty string
}

int main(int argc, char *argv[]) {
  int device_count = 0;
  char dev_path[MAX_AT_RESPONSE]
               [MAX_DEVICE_PATH];     // Array to store device paths
  pthread_t threads[MAX_AT_RESPONSE]; // Array to store thread IDs
  int thread_creation_result;

  sleep(5); //waiting for systemctl...le910cx.service and Modemanager

  if (find_tty_acm(argc, argv, &device_count, dev_path) != 0) {
    fprintf(stderr, "find_tty_acm failed\n");
    exit(EXIT_FAILURE);
  }

  printf("Loop count: %d\n", device_count);
  if (device_count == 0) {
    printf("No devices found. Exiting.\n");
    exit(EXIT_SUCCESS);
  }

  // Create threads for each device found
  for (int i = 0; i < device_count; i++) {
    printf("Creating thread for device: %s\n", dev_path[i]);
    thread_creation_result =
        pthread_create(&threads[i], NULL, process_device, (void *)dev_path[i]);
    if (thread_creation_result != 0) {
      fprintf(stderr, "ERROR; return code from pthread_create() is %d\n",
              thread_creation_result);
      exit(EXIT_FAILURE);
    }
  }

  // Wait for all threads to complete
  for (int i = 0; i < device_count; i++) {
    pthread_join(threads[i], NULL);
  }

  exit(EXIT_SUCCESS);
}

compile
$ gcc enable_gnss.c -o enable_gnss -Wall -Wextra -pthread -ludev

$ ./enable_gnss 1bc7 1031 02

$ cat /etc/systemd/system/le910cx.service
[Unit]
Description=enGNSS_NMEA_LE910Cx
After=ModemManager.service
After=network.target

[Service]
ExecStart=/home/nvidia/gnss/enable_gnss 1bc7 1031 02
Type=oneshot
RemainAfterExit=yes
User=root
Group=root

[Install]
WantedBy=multi-user.target

reboot and check it
//$ sudo systemctl daemon-reload

reboot


$ sudo systemctl start le910cx.service


$ sudo systemctl status le910cx.service
● le910cx.service - enGNSS_NMEA_LE910Cx
     Loaded: loaded (/etc/systemd/system/le910cx.service; disabled; vendor pres>
     Active: active (exited) since Tue 2025-05-20 08:30:16 UTC; 8s ago
    Process: 2228 ExecStart=/home/nvidia/gnss/enable_gnss 1bc7 1031 02 (code=ex>
   Main PID: 2228 (code=exited, status=0/SUCCESS)
        CPU: 23ms

May 20 08:30:11 tegra-ubuntu systemd[1]: Starting enGNSS_NMEA_LE910Cx...
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Found device: /dev/ttyUSB2
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Loop count: 1
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Creating thread for device: /de>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Thread processing device: /dev/>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Response to 'at$GPSP=1': at$GPS>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: OK
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Response to 'AT$GPSNMUN=1,1,1,1>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: OK
May 20 08:30:16 tegra-ubuntu systemd[1]: Finished enGNSS_NMEA_LE910Cx.


開機啟動
$ sudo systemctl enable le910cx.service


ref:
1. ModemManager-filter

2025年4月29日 星期二

eSIM with LPCA on Ubuntu

拿了片Thales eSIM實體卡,插在轉板上+FN990A28模組
可以用AT+ESIMID=1取得eID,但無法用lpca取得任何eSIM資訊

另外,at+cpin?時,總是會發生+CME ERROR: 14 (SIM busy)
查資料後,說是跟硬體有關

lpac
$ git clone https://github.com/estkme-group/lpac.git
$ cmake -B build_x86 -DLPAC_WITH_APDU_QMI=ON
$ cd build_x86
$ make
...
...
ouput/lpac
不知道是不是cpin有問題,才無法讀取...
$ export LPAC_APDU=qmi
$ export QMI_DEVICE=/dev/cdc-wdm1

$ sudo -E lpac chip info
error: allocate QMI client failed: CID allocation failed in the CTL client: Transaction timed out
{"type":"lpa","payload":{"code":-1,"message":"euicc_init","data":""}}

$ sudo -E lpac chip info
error: open logical channel operation failed: QMI protocol error (82): 'AccessDenied'
{"type":"lpa","payload":{"code":-1,"message":"euicc_init","data":""}}

$ qmicli --version
qmicli 1.37.0
Copyright (C) 2012-2023 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.


ref:
how-to-use-an-esim-in-linux/
/*nice*/