Dell DW5821e-eSIM & DW5829e-eSIM on Linux

Dell DW5821e-eSIM over PPP

USB 413c:81e0 (81d7 is the non-eSIM DW5821e), Foxconn T77W968, Qualcomm Snapdragon X20, firmware T77W968.F1.0.0.5.2.GC.013. A single eUICC, no physical SIM slot.

1. Serial ports

option exposes four ttyUSB ports. udev/78-dell-dw5821e.rules gives them stable names by USB interface number so scripts never guess:

symlink interface use
/dev/dw5821e-at 2 AT commands and PPP data (ATD*99#)
/dev/dw5821e-at2 3 second AT port — diagnostics while PPP holds the first (+CGCONTRDP, +CSQ, SMS)
/dev/dw5821e-gps 4 NMEA
/dev/dw5821e-dm 5 Qualcomm DM

cdc_mbim also registers wwan0/cdc-wdm0; we do not use MBIM data on this modem (untested, not needed), but qmicli --device-open-mbim --dms-set-operating-mode=reset on cdc-wdm0 works as a last-resort reset.

2. lpac over AT+CSIM

export LPAC_APDU=at_csim LPAC_APDU_AT_DEVICE=/dev/dw5821e-at LPAC_HTTP=curl
lpac profile list

The AT port must be exclusively free: stop pppd, check with fuser, kill a stuck lpac. Leftover /var/lock/LCK..ttyUSB0 files from a dead pppd block the next dial — the scripts remove them.

3. Switching a profile — the full cycle

scripts/dw5821e-ppp/esim-switch-profile-ppp.sh <AID> <APN>:

  1. stop pppd, free the AT port, close stale logical channels on the eUICC;
  2. step 2b — AT+CFUN? before touching lpac. If a previous cycle died between CFUN=0 and CFUN=1 the radio is off, the eUICC is unpowered and lpac returns nothing; the script sends AT+CFUN=1 and waits for +CPIN: READY first;
  3. lpac profile list → disable current → enable target;
  4. step 4b — AT+CFUN=0, 2 s, AT+CFUN=1; poll AT+CREG? for ,1/,5 (3 s after CFUN=1 on our network);
  5. step 4c — esim-ensure-apn.sh <APN>: the attach that CFUN=1 triggers uses whatever APN context 1 held before, i.e. the previous operator’s; the modem answers ERROR to AT+CGDCONT while CFUN=0. The helper sets AT+CGDCONT=1,"IP","<APN>" with the radio on, reads +CGCONTRDP=1, and if the active context carries another APN does AT+CGATT=0 → AT+CGATT=1 (≈14 s) and re-checks. It uses the second AT port so it never competes with pppd. Called from the switch, bearer-up and refresh paths;
  6. write the PPP peer and chatscript for the APN, pppd call esim-auto, wait for ppp0 to get an address, routing table 100 + rule, namespace.

Whole switch: ≈80 s.

4. PPP peer and chatscript

Generated by _ppp-peer-template.sh (regenerated on every bring-up — a hand-edited peer with a wrong device path once sent a recovery ladder through seven escalations for nothing):

/dev/dw5821e-at
115200
connect "/usr/sbin/chat -v -f /etc/ppp/chatscripts/esim-auto"
noauth usepeerdns noipdefault nopcomp noaccomp novj nobsdcomp nodeflate
maxfail 5
holdoff 10
ABORT "BUSY"  ABORT "NO CARRIER"  ABORT "ERROR"
"" AT
OK ATH
OK AT+CGDCONT=1,"IP","<APN>"
OK ATD*99#
CONNECT ""

Expect Could not determine remote IP address: defaulting to 10.64.64.64 and IPV6CP: timeout sending Config-Requests in the pppd log — both harmless. PAP succeeds with empty credentials on the Russian operators we use.

5. Health and refresh

esim-bearer-refresh-ppp.sh <APN> kills pppd, drops ppp0, removes stale lock files, ensures the attach APN and dials again. A PPP session that negotiated IPCP but shows Sent N bytes, received 0 bytes on hangup never carried payload — look at the attach APN (+CGCONTRDP=1 on the second port) and at the operator, not at pppd.