Skip to content

Instantly share code, notes, and snippets.

@trevormjaymes
Last active March 10, 2026 04:52
Show Gist options
  • Select an option

  • Save trevormjaymes/45ab8e596bf3a1ba858ef92e6b27909b to your computer and use it in GitHub Desktop.

Select an option

Save trevormjaymes/45ab8e596bf3a1ba858ef92e6b27909b to your computer and use it in GitHub Desktop.

Manual Carrier Switching

How to switch the Orbic RC400L between carriers using only a root shell and AT commands. No custom firmware required — just the stock device with root access.

Overview

The Orbic RC400L ships with Verizon/Kajeet PDP context profiles baked in. When you insert a different carrier's SIM (T-Mobile, AT&T, etc.), the modem detects the new network but the old APN configurations remain, preventing a data connection. You must manually delete the old PDP contexts, configure new ones, update the QCMAP config file, and reboot.

Why AT+CGACT doesn't work: The stock QCMAP_ConnectionManager daemon owns the data call. Attempting AT+CGACT=1,<cid> returns +CME ERROR: unknown. Instead, we configure the APN at both the AT command level and in QCMAP's config file, then reboot to let QCMAP establish the connection cleanly.

Two things must be changed:

  1. Modem PDP contexts (via AT commands) — tells the modem which APN to use
  2. QCMAP config file (/usrdata/data/qcmap/mobileap_cfg.xml) — tells the connection manager which APN to request

If these are out of sync, you'll get connection loops (^DATACONNECT / ^DATADISCONN cycling).

Prerequisites

Requirement Details
Root shell Port 2323 via rayhunter exploit (see Getting Root Access)
WiFi connection Connected to the device's hotspot (default: 192.168.1.1)
SIM card Activated SIM from the target carrier, inserted in the device

Carrier APN Profiles

These are the known-working APN configurations, tested on real devices.

T-Mobile

CID PDP Type APN
1 IPV4V6 fast.t-mobile.com

Single context. T-Mobile is the simplest — one APN handles everything. The generic fast.t-mobile.com works for all T-Mobile and T-Mobile MVNO SIMs tested (2026-02-15).

Verizon

CID PDP Type APN
1 IPV4V6 vzwinternet
2 IPV6 VZWAPP
3 IPV4V6 vzwinternet
6 IPV4V6 VZWEMERGENCY

Verizon requires multiple contexts. CID 1 is the primary data bearer; CID 3 is a duplicate used by IMS. CID 6 is for E911.

AT&T

CID PDP Type APN
1 IPV4V6 nxtgenphone

Single context. Tested on device 2026-02-15 with AT&T prepaid SIM (IMSI 310410...). The nxtgenphone APN is the correct one for AT&T prepaid/consumer plans. The broadband APN (sometimes auto-configured by the modem) gets an IP address but routes zero packets — do not use it.

Important: AT&T prepaid SIMs must be activated at att.com/prepaid/activations or att.com/activateprepaid before they will pass data. An unactivated SIM will connect to the network (get an IP) but all HTTP traffic is redirected to the activation portal.

Kajeet (Original / Verizon MVNO)

CID PDP Type APN
1 IPV4V6 LAUSDFG.GW5.VZWENTP
2 IPV6 VZWAPP
3 IPV4V6 LAUSDFG.GW5.VZWENTP
4 IPV4V6 VZWAPP
6 IPV4V6 VZWEMERGENCY

This is the factory default for Kajeet SmartSpot units. The enterprise APN LAUSDFG.GW5.VZWENTP will only work with an active Kajeet account.


Backup Current Configuration

Always save your current configuration before switching carriers. This lets you restore if something goes wrong.

Save QCMAP Config

# On the device (via root shell):
cp /usrdata/data/qcmap/mobileap_cfg.xml /usrdata/data/qcmap/mobileap_cfg.xml.bak

Save Current PDP Contexts

# Record current AT-level PDP contexts:
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# Save the output somewhere — you'll need the CID numbers, PDP types, and APN names to restore

Restore Previous Configuration

To revert to a previous carrier configuration:

# Restore QCMAP config
cp /usrdata/data/qcmap/mobileap_cfg.xml.bak /usrdata/data/qcmap/mobileap_cfg.xml

# Then follow the normal carrier switch procedure to set the AT-level PDP contexts back
# and reboot (Step 9)

Step-by-Step Procedure

Step 1: Get Root Access

Spawn a root shell.

get a root shell

Verify the shell is up:

nc 192.168.1.1 2323
# You should see no prompt — type 'id' and press Enter
id
# Expected: uid=0(root) gid=0(root)

Step 2: Sending AT Commands

The modem's AT command port is /dev/smd7. It's a character device with no line buffering — you need to use a background reader pattern. Send commands one at a time. Rapid-fire commands will crash the modem.

The pattern for every AT command in this guide:

# Template — replace CMD with the actual AT command
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'CMD\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Test it with a simple command first:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'ATI\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Expected output:

Manufacturer: QUALCOMM INCORPORATED
Model: 0
Revision: MPSS.JO.2.0.2.c1.1-00032-9607_GENNS_PACK-1  1  [Sep 01 2017 01:00:00]
SVN: 03
IMEI: 861064040xxxxxx
+GCAP: +CGSM,+DS,+ES

OK

If you get no output, wait 5 seconds and try again. The /dev/smd7 device can only have one reader at a time.

Step 3: Check Current State

Read SIM identity (IMSI)

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CIMI\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Example output for T-Mobile:

3102604215xxxxx

OK

The first 5-6 digits tell you the carrier:

  • 31026x = T-Mobile
  • 31148x or 31048x = Verizon
  • 31041x = AT&T

Check existing PDP contexts

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Example output (device still has Verizon config with T-Mobile SIM):

+CGDCONT: 1,"IPV4V6","fast.t-mobile.com","0.0.0.0",0,0,0,0
+CGDCONT: 2,"IPV6","VZWAPP","0.0.0.0",0,0,0,0
+CGDCONT: 3,"IPV4V6","vzwinternet","0.0.0.0",0,0,0,0
+CGDCONT: 4,"IPV4V6","VZWAPP","0.0.0.0",0,0,0,0
+CGDCONT: 6,"IPV4V6","VZWEMERGENCY","0.0.0.0",0,0,0,0

OK

CIDs 2, 3, 4, 6 are leftover Verizon contexts that need to be removed.

Check which contexts are active

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGACT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Example output:

+CGACT: 1,0
+CGACT: 2,0
+CGACT: 3,1
+CGACT: 6,0

OK

The ,1 at the end means active. Here CID 3 (vzwinternet) is active — the old Verizon APN grabbed the T-Mobile radio, which won't work properly.

Check network registration

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CEREG?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
+CEREG: 0,1    <- registered, home network

States: 0=not registered, 1=home, 2=searching, 3=denied, 5=roaming

Check current IP address

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGPADDR=1\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
+CGPADDR: 1,"28.183.217.80"

OK

If you see 0.0.0.0 or no address, data isn't connected.

Step 4: Delete Old PDP Contexts

Delete every context that doesn't belong to the target carrier. To delete a context, send AT+CGDCONT=<cid> with no other parameters — this removes that CID entirely.

Wait 3 seconds between each command. Do not rush.

Example: switching from Verizon/Kajeet to T-Mobile. Keep CID 1 (we'll reconfigure it), delete 2, 3, 4, 6:

# Delete CID 2
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=2\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# Expected: OK

# Wait between commands
sleep 3

# Delete CID 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=3\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

sleep 3

# Delete CID 4
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=4\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

sleep 3

# Delete CID 6
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=6\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

All should return OK. If a CID doesn't exist, you'll get ERROR — that's fine, it just means it was already gone.

Step 5: Configure New PDP Contexts

Set up the PDP contexts for the target carrier. Format:

AT+CGDCONT=<cid>,"<pdp_type>","<apn>"

For T-Mobile:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","fast.t-mobile.com"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# Expected: OK

That's it — one command for T-Mobile.

For Verizon:

# CID 1 - Primary internet
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","vzwinternet"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# CID 2 - App services
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=2,"IPV6","VZWAPP"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# CID 3 - Secondary internet (IMS)
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=3,"IPV4V6","vzwinternet"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# CID 6 - Emergency services
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=6,"IPV4V6","VZWEMERGENCY"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

For AT&T:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","nxtgenphone"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# Expected: OK

Do NOT use broadband — it connects but routes zero packets. nxtgenphone is the correct AT&T APN.

Step 6: Verify Configuration

Confirm the new contexts are set:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

For T-Mobile you should see only:

+CGDCONT: 1,"IPV4V6","fast.t-mobile.com","0.0.0.0",0,0,0,0

OK

No leftover Verizon CIDs.

Step 7: Update QCMAP Config

This is critical. AT commands alone are not enough. The QCMAP_ConnectionManager daemon reads its APN from a config file and will try to establish a data session using whatever APN is in that file, regardless of what AT-level PDP contexts are set.

# Check current QCMAP APN:
grep -i apn /usrdata/data/qcmap/mobileap_cfg.xml

You'll see something like:

<APN>LAUSDFG.GW5.VZWENTP</APN>

Change it to match the new carrier's primary APN:

For T-Mobile:

sed -i 's|<APN>[^<]*</APN>|<APN>fast.t-mobile.com</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml

For Verizon:

sed -i 's|<APN>[^<]*</APN>|<APN>vzwinternet</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml

For AT&T:

sed -i 's|<APN>[^<]*</APN>|<APN>nxtgenphone</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml

Also ensure AutoConnect is enabled:

sed -i 's|<AutoConnect>0</AutoConnect>|<AutoConnect>1</AutoConnect>|' /usrdata/data/qcmap/mobileap_cfg.xml

Verify the changes:

grep -E '<APN>|<AutoConnect>' /usrdata/data/qcmap/mobileap_cfg.xml

Step 8: Enable Mobile Data (if disabled)

The stock device may have mobile data disabled. The quickest check is via the GoAhead stock web API (port 80):

# Check if mobile data is enabled (from the device shell):
# If you see "status":0 in the response, data is off

If you have HTTP access to GoAhead (port 80), enable it via the stock API. See Alternative: GoAhead Stock Web API below.

Step 9: Reboot

Do NOT kill QCMAP_ConnectionManager directly — it manages the WiFi bridge, and killing it drops WiFi immediately with no way to reconnect except via ADB USB or physical reboot.

Instead, reboot the device cleanly:

reboot

After reboot (~30 seconds), reconnect to the device WiFi and re-establish the root shell:

nc 192.168.1.1 2323

Step 10: Verify Data Connection

Wait a few seconds for QCMAP to establish the data bearer, then check:

sleep 5

# Check for IP address
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGPADDR=1\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Expected:

+CGPADDR: 1,"100.138.59.177"

OK

Any non-zero, non-0.0.0.0 address means the data connection is up.

Verify registration:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CEREG?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
+CEREG: 0,1    <- home network

Or +CEREG: 0,5 if roaming.

Check the operator name:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+COPS?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
+COPS: 0,0,"T-Mobile",7

OK

The 7 at the end means E-UTRAN (LTE).

Check the Linux network interface:

ifconfig rmnet_data0

You should see UP RUNNING with an assigned IP address and non-zero RX packets.

At this point the device should be serving data on the new carrier. Connect a WiFi client and test.


Complete Example: Verizon -> T-Mobile

This is the exact sequence that was tested and proven on a live device (2026-02-15). SIM already swapped, device booted.

# 1. Get a shell
# (from your machine, connected to device WiFi)
nc 192.168.1.1 2323

# 2. Backup current config
cp /usrdata/data/qcmap/mobileap_cfg.xml /usrdata/data/qcmap/mobileap_cfg.xml.bak

# 3. Confirm SIM is T-Mobile
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CIMI\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> 310260421579252  (31026 = T-Mobile)

# 4. See what's configured
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> CID 1: fast.t-mobile.com
# -> CID 2: VZWAPP          <- leftover
# -> CID 3: vzwinternet     <- leftover, ACTIVE
# -> CID 4: VZWAPP          <- leftover
# -> CID 6: VZWEMERGENCY    <- leftover

# 5. Delete old Verizon contexts
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=2\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=3\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=4\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=6\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# 6. Configure T-Mobile APN (CID 1 may already be correct from SIM auto-config)
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","fast.t-mobile.com"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# 7. Verify clean config
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> Only CID 1: fast.t-mobile.com

# 8. Update QCMAP config
sed -i 's|<APN>[^<]*</APN>|<APN>fast.t-mobile.com</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml
sed -i 's|<AutoConnect>0</AutoConnect>|<AutoConnect>1</AutoConnect>|' /usrdata/data/qcmap/mobileap_cfg.xml

# 9. Reboot
reboot
# Wait ~30s, reconnect WiFi, re-spawn shell

# 10. Verify
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGPADDR=1\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGPADDR: 1,"100.138.59.177"

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+COPS?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +COPS: 0,0,"T-Mobile",7

Total time: ~90 seconds from shell to verified data connection (including reboot).


Complete Example: T-Mobile -> Verizon

# 1. Swap SIM (power off, swap, power on, connect WiFi, get shell)

# 2. Backup config
cp /usrdata/data/qcmap/mobileap_cfg.xml /usrdata/data/qcmap/mobileap_cfg.xml.bak

# 3. Confirm SIM is Verizon
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CIMI\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> 311480xxxxxxxxx  (31148 = Verizon)

# 4. Delete old T-Mobile context (only CID 1 if clean)
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGDCONT: 1,"IPV4V6","fast.t-mobile.com",...
# CID 1 will be overwritten, no need to delete

# 5. Configure Verizon contexts
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","vzwinternet"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=2,"IPV6","VZWAPP"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=3,"IPV4V6","vzwinternet"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=6,"IPV4V6","VZWEMERGENCY"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# 6. Update QCMAP config
sed -i 's|<APN>[^<]*</APN>|<APN>vzwinternet</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml
sed -i 's|<AutoConnect>0</AutoConnect>|<AutoConnect>1</AutoConnect>|' /usrdata/data/qcmap/mobileap_cfg.xml

# 7. Reboot
reboot
# Wait ~30s, reconnect WiFi, re-spawn shell

# 8. Verify
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGATT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGATT: 1

sleep 5
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGPADDR=1\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGPADDR: 1,"28.167.107.145"

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+COPS?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +COPS: 0,0,"Verizon",7

Complete Example: Verizon/Kajeet -> AT&T

Tested 2026-02-15 with AT&T prepaid SIM.

# 1. Swap SIM, power on, connect WiFi, get shell
nc 192.168.1.1 2323

# 2. Backup config
cp /usrdata/data/qcmap/mobileap_cfg.xml /usrdata/data/qcmap/mobileap_cfg.xml.bak

# 3. Confirm SIM is AT&T
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CIMI\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> 310410426347917  (31041 = AT&T)

# 4. Check current config — modem may auto-configure CID 1 with "broadband"
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGDCONT: 1,"IPV4V6","broadband","0.0.0.0",0,0,0,0
# -> +CGDCONT: 6,"IPV4V6","VZWEMERGENCY","0.0.0.0",0,0,0,0
# Note: CID 6 VZWEMERGENCY is stored in modem NV and reappears after reboot

# 5. Delete leftover Verizon emergency context
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=6\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# 6. Set correct AT&T APN (NOT broadband!)
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","nxtgenphone"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
sleep 3

# 7. Verify
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGDCONT: 1,"IPV4V6","nxtgenphone","0.0.0.0",0,0,0,0

# 8. Update QCMAP config
sed -i 's|<APN>[^<]*</APN>|<APN>nxtgenphone</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml
sed -i 's|<AutoConnect>0</AutoConnect>|<AutoConnect>1</AutoConnect>|' /usrdata/data/qcmap/mobileap_cfg.xml

# 9. Reboot
reboot
# Wait ~30s, reconnect WiFi, re-spawn shell

# 10. Verify connection
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGATT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGATT: 1

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGPADDR=1\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGPADDR: 1,"10.191.55.176"

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+COPS?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +COPS: 0,0,"AT&T",7

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CSQ\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CSQ: 31,99  (excellent signal)

# 11. Check Linux interface
ifconfig rmnet_data0
# -> Should show UP RUNNING with assigned IP and non-zero RX packets
# If RX packets is 0, the SIM may need activation (see AT&T note above)

Note: If ping 8.8.8.8 fails but you have an IP, check for carrier HTTP redirect:

# Raw HTTP test (from device shell):
printf 'GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n' | nc 142.250.80.46 80
# If you see "302 Found" + "Location: https://www.att.com/prepaid/activations/"
# then the SIM is not activated yet

Alternative: GoAhead Stock Web API

The stock GoAhead web server on port 80 provides another way to manage data connections. This is useful when you can't use AT commands or need to toggle mobile data on/off.

Authentication

GoAhead requires authentication. From the device shell:

# Get the auth key:
curl -s http://127.0.0.1/goform/GetLoginInfo
# Response: {"priKey":"<some_key>"}

Enable/Disable Mobile Data

# Enable mobile data:
curl -s -X POST http://127.0.0.1/action/SetMobileDataStatus \
  -H "Content-Type: application/json" \
  -b "token=<your_token>" \
  -d '{"status":1}'

# Disable mobile data:
curl -s -X POST http://127.0.0.1/action/SetMobileDataStatus \
  -H "Content-Type: application/json" \
  -b "token=<your_token>" \
  -d '{"status":0}'

Check Connection Status

curl -s http://127.0.0.1/action/GetMobileStatusInfo -b "token=<your_token>"
# Returns: mode, signal, ip, connection_time, uptime, etc.

Limitations

  • The GoAhead profile system is read-only — you cannot change the APN through SetProfile or AddProfile. These calls crash GoAhead.
  • To change the APN, you must edit the QCMAP config file directly (Step 7 above).
  • GoAhead always shows its built-in profile (e.g., broadband for AT&T SIMs) regardless of what QCMAP is actually using.

Recovery: WiFi Down, ADB Available

If WiFi goes down (e.g., QCMAP was killed), USB ADB provides a backup path.

ADB Port Forwarding to GoAhead

# From your Mac:
adb forward tcp:8080 tcp:80

# Now access GoAhead via localhost:
curl http://localhost:8080/goform/GetLoginInfo

Troubleshooting

No response from /dev/smd7

Another process may be reading the device. Kill any existing readers:

# Check for processes using smd7
fuser /dev/smd7
# Kill if found
kill <pid>

Wait 5 seconds, then retry.

AT+CGATT=1 returns ERROR or +CGATT stays 0

The modem can't find the network. This happens when:

  1. SIM not recognized — check AT+CIMI returns a valid IMSI
  2. Wrong APN — verify AT+CGDCONT? shows the correct APNs for your carrier
  3. Weak signal — try AT+CSQ to check signal strength (10+ is usable, 20+ is good)
  4. Rapid switching — the modem needs time between detach/attach cycles. Wait 5-10 seconds and try again. It consistently works by the 2nd or 3rd attempt.

Retry pattern:

# Wait longer, then try again
sleep 5
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGATT=1\r' > /dev/smd7; sleep 3; kill %1 2>/dev/null; cat /tmp/at_out
sleep 2
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGATT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

Got IP but no internet (zero RX packets)

Several possible causes:

  1. Wrong APN — most common cause. For AT&T, broadband gets an IP but doesn't route. Use nxtgenphone.
  2. SIM not activated — carrier assigns an IP but redirects all traffic. Test with raw HTTP:
    printf 'GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n' | nc 142.250.80.46 80
    If you see a 302 redirect to your carrier's activation page, the SIM needs activation.
  3. QCMAP not bridging — check ifconfig rmnet_data0. If it shows NO FLAGS or no IP, QCMAP isn't managing the connection. Verify QCMAP config has <AutoConnect>1</AutoConnect> and reboot.
  4. QCMAP/modem APN mismatch — if QCMAP config says one APN but modem has another, you'll get connection cycling. Make sure both match (Step 7).

Got IP but no internet (RX packets > 0, ping fails)

Traffic is flowing but being intercepted:

  1. Carrier captive portal — unactivated SIM. See "SIM not activated" above.
  2. DNS failure — try pinging by IP: ping -c1 8.8.8.8. If that works but DNS doesn't, check /tmp/resolv_rmnet_data.conf for carrier DNS servers and verify dnsmasq is forwarding.

QCMAP and modem APN out of sync

Symptoms: ^DATACONNECT immediately followed by ^DATADISCONN in dmesg, connection cycling.

Fix: Make sure the APN in QCMAP config matches the AT-level CID 1 APN, then reboot.

# Check QCMAP APN
grep '<APN>' /usrdata/data/qcmap/mobileap_cfg.xml

# Check AT-level APN
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

WiFi dropped after killing QCMAP

Never killall QCMAP_ConnectionManager — it manages the WiFi bridge. Killing it drops WiFi immediately.

Recovery:

  1. ADB USBadb forward tcp:8080 tcp:80 to access GoAhead, then reboot via command injection (see Recovery section above)
  2. Physical reboot — hold power button, or unplug/replug USB power

+CME ERROR: Requested service option not subscribed (Cause 33)

The carrier rejected the APN. This means:

  • The APN name is wrong for your SIM/plan
  • The SIM isn't activated on the carrier's network
  • The carrier requires a specific APN that differs from the generic one

Check with AT+CEER for the detailed rejection cause:

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CEER\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out

PDP contexts reappear after reboot

Some PDP contexts are stored in modem NV memory. The Verizon VZWEMERGENCY (CID 6) is particularly persistent — it reappears after every reboot even when deleted. This is harmless; just ensure CID 1 has the correct APN.

The QCMAP config file changes DO persist across reboots (stored on /usrdata/ partition).

GoAhead crashes on profile changes

The stock GoAhead web API crashes (connection drops) when you try SetProfile or AddProfile for the built-in readonly profile. Don't use these endpoints. Edit the QCMAP config file directly instead.

GoAhead HTTP/1.1 issues

Python requests library (HTTP/1.1 with keepalive) causes GoAhead to drop connections. Use raw HTTP/1.0 sockets or curl instead.


Quick Reference

SIM Identification (IMSI Prefix -> Carrier)

IMSI Prefix Carrier
31026 T-Mobile
31020-31025, 31027 T-Mobile (alternate)
31148, 31048 Verizon
31041 AT&T

APN Quick Reference

Carrier APN Notes
T-Mobile fast.t-mobile.com Works for MVNOs too
Verizon vzwinternet Primary; also needs VZWAPP + VZWEMERGENCY
AT&T nxtgenphone Do NOT use broadband
Kajeet LAUSDFG.GW5.VZWENTP Enterprise only

Config File Locations

File Purpose
/usrdata/data/qcmap/mobileap_cfg.xml QCMAP APN and AutoConnect config
/usrdata/data/usr/goahead/default_pwd GoAhead/WiFi password
/data/dnsmasq.conf DNS forwarder config
/tmp/resolv_rmnet_data.conf Carrier DNS servers (auto-populated)

AT Command Summary

Command Purpose
AT+CIMI Read SIM IMSI
AT+CGSN Read IMEI (just the number)
ATI Read IMEI + device info
AT+CGDCONT? List configured PDP contexts
AT+CGACT? List active PDP contexts
AT+CGDCONT=<cid> Delete PDP context
AT+CGDCONT=<cid>,"<type>","<apn>" Configure PDP context
AT+CGATT=0 Detach from network
AT+CGATT=1 Attach to network
AT+CGATT? Check attach status
AT+CGPADDR=1 Check IP address on CID 1
AT+COPS? Check registered operator
AT+CEREG? Check LTE registration status
AT+CSQ Check signal strength
AT+CEER Last error/rejection cause

Minimum Viable Switch (T-Mobile, one-liner)

If you're in a hurry and know the device has a T-Mobile SIM:

# Delete all non-CID-1 contexts, set CID 1 to T-Mobile, update QCMAP, reboot
for cid in 2 3 4 5 6; do printf "AT+CGDCONT=$cid\r" > /dev/smd7; sleep 1; done; \
printf 'AT+CGDCONT=1,"IPV4V6","fast.t-mobile.com"\r' > /dev/smd7; sleep 1; \
sed -i 's|<APN>[^<]*</APN>|<APN>fast.t-mobile.com</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml; \
sed -i 's|<AutoConnect>0</AutoConnect>|<AutoConnect>1</AutoConnect>|' /usrdata/data/qcmap/mobileap_cfg.xml; \
reboot

Warning: This fires commands without reading responses and reboots immediately. Use the step-by-step procedure above when debugging.


Complete Example: AT&T -> T-Mobile

AT&T SIM swapped out, T-Mobile SIM inserted, device booted.

# 1. Spawn shell
nc 192.168.1.1 2323

# 2. Backup config (was AT&T nxtgenphone)
cp /usrdata/data/qcmap/mobileap_cfg.xml /usrdata/data/qcmap/mobileap_cfg.xml.bak.att

# 3. Confirm SIM is T-Mobile
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CIMI\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> 310260421579252  (31026 = T-Mobile)

# 4. Check current config — modem auto-configured CID 1 with correct APN!
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGDCONT: 1,"IPV4V6","fast.t-mobile.com","0.0.0.0...",0,0,0,0
# -> +CGDCONT: 6,"IPV4V6","VZWEMERGENCY","0.0.0.0...",0,0,0,1
# Note: CID 1 auto-detected correctly. CID 6 VZWEMERGENCY persists in modem NV.

# 5. Delete leftover Verizon emergency context
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=6\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> OK
sleep 3

# 6. CID 1 already has fast.t-mobile.com — set it explicitly to be safe
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGDCONT=1,"IPV4V6","fast.t-mobile.com"\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> OK
sleep 3

# 7. Update QCMAP config (was nxtgenphone from AT&T)
sed -i 's|<APN>[^<]*</APN>|<APN>fast.t-mobile.com</APN>|' /usrdata/data/qcmap/mobileap_cfg.xml
sed -i 's|<AutoConnect>0</AutoConnect>|<AutoConnect>1</AutoConnect>|' /usrdata/data/qcmap/mobileap_cfg.xml

# 8. Verify QCMAP
grep -E '<APN>|<AutoConnect>' /usrdata/data/qcmap/mobileap_cfg.xml
# -> <AutoConnect>1</AutoConnect>
# -> <APN>fast.t-mobile.com</APN>

# 9. Check status (no reboot needed — CID 1 was already active)
(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGACT?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGACT: 1,1  (CID 1 active)

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CEREG?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CEREG: 0,1  (registered, home)

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+COPS?\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +COPS: 0,0,"T-Mobile",7  (LTE)

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CGPADDR=1\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CGPADDR: 1,100.243.217.154,...  (CGNAT IPv4 + IPv6)

(cat /dev/smd7 > /tmp/at_out &); sleep 0.3; printf 'AT+CSQ\r' > /dev/smd7; sleep 2; kill %1 2>/dev/null; cat /tmp/at_out
# -> +CSQ: 28,99  (good signal)

# 10. Check Linux interface
ifconfig rmnet_data0
# -> UP RUNNING, inet 100.243.217.154, inet6 2607:fb90:...
# -> RX packets:9 (low — SIM activation still processing)

route -n
# -> Default route via 100.243.217.153 on rmnet_data0

# 11. Test connectivity
ping -c3 8.8.8.8
# -> 100% packet loss (SIM activation still processing)

cat /tmp/resolv_rmnet_data.conf
# -> nameserver 10.177.0.34
# -> nameserver 10.177.0.210
# -> nameserver fd00:976a::9
# -> nameserver fd00:976a::10

Result: Device registered on T-Mobile LTE with valid CGNAT IP and IPv6, QCMAP bridging active, default route configured. Ping fails because SIM activation is still processing. Once activation completes, data should flow without any further configuration changes.

Key observation: When switching to T-Mobile, the modem auto-configured CID 1 with fast.t-mobile.com — no manual APN setup was needed at the AT level. The QCMAP config update was still required since it had the previous carrier's APN.


QCMAP Configuration Reference

The full QCMAP config file at /usrdata/data/qcmap/mobileap_cfg.xml:

<?xml version="1.0"?>
<system xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="mobileap_cfg.xsd">
  <MobileAPCfg>
    <MobileAPNatCfg>
      <Firewall>/data/mobileap_firewall.xml</Firewall>
      <NatEntryGenericTimeout>200</NatEntryGenericTimeout>
      <NatEntryICMPTimeout>30</NatEntryICMPTimeout>
      <NatEntryTCPEstablishedTimeout>3600</NatEntryTCPEstablishedTimeout>
      <NatEntryUDPTimeout>60</NatEntryUDPTimeout>
      <DmzIP>0.0.0.0</DmzIP>
      <EnableIPSECVpnPassthrough>1</EnableIPSECVpnPassthrough>
      <EnablePPTPVpnPassthrough>1</EnablePPTPVpnPassthrough>
      <EnableL2TPVpnPassthrough>1</EnableL2TPVpnPassthrough>
      <EnableWebserverWWANAccess>0</EnableWebserverWWANAccess>
      <NATType>SYM</NATType>
      <!-- ... ALG, limits ... -->
    </MobileAPNatCfg>
    <MobileAPLanCfg>
      <EnableIPV4>1</EnableIPV4>
      <EnableIPV6>1</EnableIPV6>
      <WlanMode>AP</WlanMode>
      <APIPAddr>192.168.1.1</APIPAddr>
      <SubNetMask>255.255.255.0</SubNetMask>
      <EnableDHCPServer>1</EnableDHCPServer>
      <DHCPCfg>
        <StartIP>192.168.1.100</StartIP>
        <EndIP>192.168.1.200</EndIP>
        <LeaseTime>86400</LeaseTime>
      </DHCPCfg>
      <!-- ... hostapd, guest AP, STA mode ... -->
    </MobileAPLanCfg>
    <MobileAPWanCfg>
      <!-- === THESE ARE THE FIELDS YOU CHANGE === -->
      <AutoConnect>1</AutoConnect>      <!-- Must be 1 for auto data -->
      <Roaming>1</Roaming>
      <TECH>0</TECH>
      <V4_UMTS_PROFILE_INDEX>1</V4_UMTS_PROFILE_INDEX>
      <V4_CDMA_PROFILE_INDEX>0</V4_CDMA_PROFILE_INDEX>
      <V6_UMTS_PROFILE_INDEX>1</V6_UMTS_PROFILE_INDEX>
      <V6_CDMA_PROFILE_INDEX>0</V6_CDMA_PROFILE_INDEX>
      <APN>fast.t-mobile.com</APN>      <!-- Must match AT+CGDCONT CID 1 -->
      <UserName></UserName>
      <PassWord />
      <AUTH>0</AUTH>
      <PrefixDelegation>0</PrefixDelegation>
      <!-- ... backhaul priority, SIP, ERI ... -->
    </MobileAPWanCfg>
    <!-- ... services (UPnP, DLNA, DDNS), boot config ... -->
  </MobileAPCfg>
</system>

The critical fields for carrier switching are in <MobileAPWanCfg>:

  • <AutoConnect>1</AutoConnect> — must be 1 or QCMAP won't auto-establish the data call
  • <APN>...</APN> — must match the AT-level CID 1 APN exactly
  • <V4_UMTS_PROFILE_INDEX>1</V4_UMTS_PROFILE_INDEX> — maps to CID 1
  • <AUTH>0</AUTH> — no authentication (correct for T-Mobile, AT&T, Verizon consumer)
@Smith8154
Copy link

Yes, this is amazing! I was able to get my device activated on Telnyx. Would be happy to contribute anything I can.

@lusca-cs
Copy link

Thank you! It now works in The Bahamas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment