49fd87ff9d100a823c6e3bfe2e67df80dc73dc57
[openwrt/openwrt.git] / package / network / utils / uqmi / files / lib / netifd / proto / qmi.sh
1 #!/bin/sh
2
3 [ -n "$INCLUDE_ONLY" ] || {
4 . /lib/functions.sh
5 . ../netifd-proto.sh
6 init_proto "$@"
7 }
8
9 proto_qmi_init_config() {
10 available=1
11 no_device=1
12 proto_config_add_string "device:device"
13 proto_config_add_string apn
14 proto_config_add_string v6apn
15 proto_config_add_string auth
16 proto_config_add_string username
17 proto_config_add_string password
18 proto_config_add_string pincode
19 proto_config_add_int delay
20 proto_config_add_string modes
21 proto_config_add_string pdptype
22 proto_config_add_int profile
23 proto_config_add_int v6profile
24 proto_config_add_boolean dhcp
25 proto_config_add_boolean dhcpv6
26 proto_config_add_boolean autoconnect
27 proto_config_add_int plmn
28 proto_config_add_int timeout
29 proto_config_add_int mtu
30 proto_config_add_defaults
31 }
32
33 proto_qmi_setup() {
34 local interface="$1"
35 local dataformat connstat plmn_mode mcc mnc
36 local device apn v6apn auth username password pincode delay modes pdptype
37 local profile v6profile dhcp dhcpv6 autoconnect plmn timeout mtu $PROTO_DEFAULT_OPTIONS
38 local ip4table ip6table
39 local cid_4 pdh_4 cid_6 pdh_6
40 local ip_6 ip_prefix_length gateway_6 dns1_6 dns2_6
41 local profile_pdptype
42
43 json_get_vars device apn v6apn auth username password pincode delay modes
44 json_get_vars pdptype profile v6profile dhcp dhcpv6 autoconnect plmn ip4table
45 json_get_vars ip6table timeout mtu $PROTO_DEFAULT_OPTIONS
46
47 [ "$timeout" = "" ] && timeout="10"
48
49 [ "$metric" = "" ] && metric="0"
50
51 [ -n "$ctl_device" ] && device=$ctl_device
52
53 [ -n "$device" ] || {
54 echo "No control device specified"
55 proto_notify_error "$interface" NO_DEVICE
56 proto_set_available "$interface" 0
57 return 1
58 }
59
60 [ -n "$delay" ] && sleep "$delay"
61
62 device="$(readlink -f $device)"
63 [ -c "$device" ] || {
64 echo "The specified control device does not exist"
65 proto_notify_error "$interface" NO_DEVICE
66 proto_set_available "$interface" 0
67 return 1
68 }
69
70 devname="$(basename "$device")"
71 devpath="$(readlink -f /sys/class/usbmisc/$devname/device/)"
72 ifname="$( ls "$devpath"/net )"
73 [ -n "$ifname" ] || {
74 echo "The interface could not be found."
75 proto_notify_error "$interface" NO_IFACE
76 proto_set_available "$interface" 0
77 return 1
78 }
79
80 [ -n "$mtu" ] && {
81 echo "Setting MTU to $mtu"
82 /sbin/ip link set dev $ifname mtu $mtu
83 }
84
85 echo "Waiting for SIM initialization"
86 local uninitialized_timeout=0
87 # timeout 3s for first call to avoid hanging uqmi
88 uqmi -d "$device" -t 3000 --get-pin-status > /dev/null 2>&1
89 while uqmi -s -d "$device" -t 1000 --get-pin-status | grep '"UIM uninitialized"' > /dev/null; do
90 [ -e "$device" ] || return 1
91 if [ "$uninitialized_timeout" -lt "$timeout" -o "$timeout" = "0" ]; then
92 let uninitialized_timeout++
93 sleep 1;
94 else
95 echo "SIM not initialized"
96 proto_notify_error "$interface" SIM_NOT_INITIALIZED
97 proto_block_restart "$interface"
98 return 1
99 fi
100 done
101
102 # Check if UIM application is stuck in illegal state
103 local uim_state_timeout=0
104 while true; do
105 json_load "$(uqmi -s -d "$device" -t 1000 --uim-get-sim-state)"
106 json_get_var card_application_state card_application_state
107
108 # SIM card is either completely absent or state is labeled as illegal
109 # Try to power-cycle the SIM card to recover from this state
110 if [ -z "$card_application_state" -o "$card_application_state" = "illegal" ]; then
111 echo "SIM in illegal state - Power-cycling SIM"
112
113 # Try to reset SIM application
114 uqmi -d "$device" -t 1000 --uim-power-off --uim-slot 1
115 sleep 3
116 uqmi -d "$device" -t 1000 --uim-power-on --uim-slot 1
117
118 if [ "$uim_state_timeout" -lt "$timeout" ] || [ "$timeout" = "0" ]; then
119 let uim_state_timeout++
120 sleep 1
121 continue
122 fi
123
124 # Recovery failed
125 proto_notify_error "$interface" SIM_ILLEGAL_STATE
126 proto_block_restart "$interface"
127 return 1
128 else
129 break
130 fi
131 done
132
133 if uqmi -s -d "$device" -t 1000 --uim-get-sim-state | grep -q '"Not supported"\|"Invalid QMI command"' &&
134 uqmi -s -d "$device" -t 1000 --get-pin-status | grep -q '"Not supported"\|"Invalid QMI command"' ; then
135 [ -n "$pincode" ] && {
136 uqmi -s -d "$device" -t 1000 --verify-pin1 "$pincode" > /dev/null || uqmi -s -d "$device" -t 1000 --uim-verify-pin1 "$pincode" > /dev/null || {
137 echo "Unable to verify PIN"
138 proto_notify_error "$interface" PIN_FAILED
139 proto_block_restart "$interface"
140 return 1
141 }
142 }
143 else
144 json_load "$(uqmi -s -d "$device" -t 1000 --get-pin-status)"
145 json_get_var pin1_status pin1_status
146 if [ -z "$pin1_status" ]; then
147 json_load "$(uqmi -s -d "$device" -t 1000 --uim-get-sim-state)"
148 json_get_var pin1_status pin1_status
149 fi
150 json_get_var pin1_verify_tries pin1_verify_tries
151
152 case "$pin1_status" in
153 disabled)
154 echo "PIN verification is disabled"
155 ;;
156 blocked)
157 echo "SIM locked PUK required"
158 proto_notify_error "$interface" PUK_NEEDED
159 proto_block_restart "$interface"
160 return 1
161 ;;
162 not_verified)
163 [ "$pin1_verify_tries" -lt "3" ] && {
164 echo "PIN verify count value is $pin1_verify_tries this is below the limit of 3"
165 proto_notify_error "$interface" PIN_TRIES_BELOW_LIMIT
166 proto_block_restart "$interface"
167 return 1
168 }
169 if [ -n "$pincode" ]; then
170 uqmi -s -d "$device" -t 1000 --verify-pin1 "$pincode" > /dev/null 2>&1 || uqmi -s -d "$device" -t 1000 --uim-verify-pin1 "$pincode" > /dev/null 2>&1 || {
171 echo "Unable to verify PIN"
172 proto_notify_error "$interface" PIN_FAILED
173 proto_block_restart "$interface"
174 return 1
175 }
176 else
177 echo "PIN not specified but required"
178 proto_notify_error "$interface" PIN_NOT_SPECIFIED
179 proto_block_restart "$interface"
180 return 1
181 fi
182 ;;
183 verified)
184 echo "PIN already verified"
185 ;;
186 *)
187 echo "PIN status failed (${pin1_status:-sim_not_present})"
188 proto_notify_error "$interface" PIN_STATUS_FAILED
189 proto_block_restart "$interface"
190 return 1
191 ;;
192 esac
193 json_cleanup
194 fi
195
196 if [ -n "$plmn" ]; then
197 json_load "$(uqmi -s -d "$device" -t 1000 --get-plmn)"
198 json_get_var plmn_mode mode
199 json_get_vars mcc mnc || {
200 mcc=0
201 mnc=0
202 }
203
204 if [ "$plmn" = "0" ]; then
205 if [ "$plmn_mode" != "automatic" ]; then
206 mcc=0
207 mnc=0
208 echo "Setting PLMN to auto"
209 fi
210 elif [ "$mcc" -ne "${plmn:0:3}" -o "$mnc" -ne "${plmn:3}" ]; then
211 mcc=${plmn:0:3}
212 mnc=${plmn:3}
213 echo "Setting PLMN to $plmn"
214 else
215 mcc=""
216 mnc=""
217 fi
218 fi
219
220 # Cleanup current state if any
221 uqmi -s -d "$device" -t 1000 --stop-network 0xffffffff --autoconnect > /dev/null 2>&1
222 uqmi -s -d "$device" -t 1000 --set-ip-family ipv6 --stop-network 0xffffffff --autoconnect > /dev/null 2>&1
223
224 # Go online
225 uqmi -s -d "$device" -t 1000 --set-device-operating-mode online > /dev/null 2>&1
226
227 # Set IP format
228 uqmi -s -d "$device" -t 1000 --set-data-format 802.3 > /dev/null 2>&1
229 uqmi -s -d "$device" -t 1000 --wda-set-data-format 802.3 > /dev/null 2>&1
230 dataformat="$(uqmi -s -d "$device" -t 1000 --wda-get-data-format)"
231
232 if [ "$dataformat" = '"raw-ip"' ]; then
233
234 [ -f /sys/class/net/$ifname/qmi/raw_ip ] || {
235 echo "Device only supports raw-ip mode but is missing this required driver attribute: /sys/class/net/$ifname/qmi/raw_ip"
236 return 1
237 }
238
239 echo "Device does not support 802.3 mode. Informing driver of raw-ip only for $ifname .."
240 echo "Y" > /sys/class/net/$ifname/qmi/raw_ip
241 fi
242
243 uqmi -s -d "$device" -t 1000 --sync > /dev/null 2>&1
244
245 uqmi -s -d "$device" -t 20000 --network-register > /dev/null 2>&1
246
247 # PLMN selection must happen after the call to network-register
248 if [ -n "$mcc" -a -n "$mnc" ]; then
249 uqmi -s -d "$device" -t 1000 --set-plmn --mcc "$mcc" --mnc "$mnc" > /dev/null 2>&1 || {
250 echo "Unable to set PLMN"
251 proto_notify_error "$interface" PLMN_FAILED
252 proto_block_restart "$interface"
253 return 1
254 }
255 fi
256
257 [ -n "$modes" ] && {
258 uqmi -s -d "$device" -t 1000 --set-network-modes "$modes" > /dev/null 2>&1
259 sleep 3
260 # Scan network to not rely on registration-timeout after RAT change
261 uqmi -s -d "$device" -t 30000 --network-scan > /dev/null 2>&1
262 }
263
264 echo "Waiting for network registration"
265 sleep 5
266 local registration_timeout=0
267 local registration_state=""
268 while true; do
269 registration_state=$(uqmi -s -d "$device" -t 1000 --get-serving-system 2>/dev/null | jsonfilter -e "@.registration" 2>/dev/null)
270
271 [ "$registration_state" = "registered" ] && break
272
273 if [ "$registration_state" = "searching" ] || [ "$registration_state" = "not_registered" ]; then
274 if [ "$registration_timeout" -lt "$timeout" ] || [ "$timeout" = "0" ]; then
275 [ "$registration_state" = "searching" ] || {
276 echo "Device stopped network registration. Restart network registration"
277 uqmi -s -d "$device" -t 20000 --network-register > /dev/null 2>&1
278 }
279 let registration_timeout++
280 sleep 1
281 continue
282 fi
283 echo "Network registration failed, registration timeout reached"
284 else
285 # registration_state is 'registration_denied' or 'unknown' or ''
286 echo "Network registration failed (reason: '$registration_state')"
287 fi
288
289 proto_notify_error "$interface" NETWORK_REGISTRATION_FAILED
290 return 1
291 done
292
293
294 echo "Starting network $interface"
295
296 pdptype="$(echo "$pdptype" | awk '{print tolower($0)}')"
297
298 [ "$pdptype" = "ip" -o "$pdptype" = "ipv6" -o "$pdptype" = "ipv4v6" ] || pdptype="ip"
299
300 # Configure PDP type and APN for profile 1.
301 # In case GGSN rejects IPv4v6 PDP, modem might not be able to
302 # establish a non-LTE data session.
303 profile_pdptype="$pdptype"
304 [ "$profile_pdptype" = "ip" ] && profile_pdptype="ipv4"
305 uqmi -s -d "$device" -t 1000 --modify-profile "3gpp,1" --apn "$apn" --pdp-type "$profile_pdptype" > /dev/null 2>&1
306
307 if [ "$pdptype" = "ip" ]; then
308 [ -z "$autoconnect" ] && autoconnect=1
309 [ "$autoconnect" = 0 ] && autoconnect=""
310 else
311 [ "$autoconnect" = 1 ] || autoconnect=""
312 fi
313
314 [ "$pdptype" = "ip" -o "$pdptype" = "ipv4v6" ] && {
315 cid_4=$(uqmi -s -d "$device" -t 1000 --get-client-id wds)
316 if ! [ "$cid_4" -eq "$cid_4" ] 2> /dev/null; then
317 echo "Unable to obtain client ID"
318 proto_notify_error "$interface" NO_CID
319 return 1
320 fi
321
322 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_4" --set-ip-family ipv4 > /dev/null 2>&1
323
324 pdh_4=$(uqmi -s -d "$device" -t 5000 --set-client-id wds,"$cid_4" \
325 --start-network \
326 ${apn:+--apn $apn} \
327 ${profile:+--profile $profile} \
328 ${auth:+--auth-type $auth} \
329 ${username:+--username $username} \
330 ${password:+--password $password} \
331 ${autoconnect:+--autoconnect})
332
333 # pdh_4 is a numeric value on success
334 if ! [ "$pdh_4" -eq "$pdh_4" ] 2> /dev/null; then
335 echo "Unable to connect IPv4"
336 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_4" --release-client-id wds > /dev/null 2>&1
337 proto_notify_error "$interface" CALL_FAILED
338 return 1
339 fi
340
341 # Check data connection state
342 connstat=$(uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_4" --get-data-status)
343 [ "$connstat" == '"connected"' ] || {
344 echo "No data link!"
345 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_4" --release-client-id wds > /dev/null 2>&1
346 proto_notify_error "$interface" CALL_FAILED
347 return 1
348 }
349 }
350
351 [ "$pdptype" = "ipv6" -o "$pdptype" = "ipv4v6" ] && {
352 cid_6=$(uqmi -s -d "$device" -t 1000 --get-client-id wds)
353 if ! [ "$cid_6" -eq "$cid_6" ] 2> /dev/null; then
354 echo "Unable to obtain client ID"
355 proto_notify_error "$interface" NO_CID
356 return 1
357 fi
358
359 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_6" --set-ip-family ipv6 > /dev/null 2>&1
360
361 : "${v6apn:=${apn}}"
362 : "${v6profile:=${profile}}"
363
364 pdh_6=$(uqmi -s -d "$device" -t 5000 --set-client-id wds,"$cid_6" \
365 --start-network \
366 ${v6apn:+--apn $v6apn} \
367 ${v6profile:+--profile $v6profile} \
368 ${auth:+--auth-type $auth} \
369 ${username:+--username $username} \
370 ${password:+--password $password} \
371 ${autoconnect:+--autoconnect})
372
373 # pdh_6 is a numeric value on success
374 if ! [ "$pdh_6" -eq "$pdh_6" ] 2> /dev/null; then
375 echo "Unable to connect IPv6"
376 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_6" --release-client-id wds > /dev/null 2>&1
377 proto_notify_error "$interface" CALL_FAILED
378 return 1
379 fi
380
381 # Check data connection state
382 connstat=$(uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_6" --set-ip-family ipv6 --get-data-status)
383 [ "$connstat" == '"connected"' ] || {
384 echo "No data link!"
385 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid_6" --release-client-id wds > /dev/null 2>&1
386 proto_notify_error "$interface" CALL_FAILED
387 return 1
388 }
389 }
390
391 echo "Setting up $ifname"
392 proto_init_update "$ifname" 1
393 proto_set_keep 1
394 proto_add_data
395 [ -n "$pdh_4" ] && {
396 json_add_string "cid_4" "$cid_4"
397 json_add_string "pdh_4" "$pdh_4"
398 }
399 [ -n "$pdh_6" ] && {
400 json_add_string "cid_6" "$cid_6"
401 json_add_string "pdh_6" "$pdh_6"
402 }
403 proto_close_data
404 proto_send_update "$interface"
405
406 local zone="$(fw3 -q network "$interface" 2>/dev/null)"
407
408 [ -n "$pdh_6" ] && {
409 if [ -z "$dhcpv6" -o "$dhcpv6" = 0 ]; then
410 json_load "$(uqmi -s -d $device -t 1000 --set-client-id wds,$cid_6 --get-current-settings)"
411 json_select ipv6
412 json_get_var ip_6 ip
413 json_get_var gateway_6 gateway
414 json_get_var dns1_6 dns1
415 json_get_var dns2_6 dns2
416 json_get_var ip_prefix_length ip-prefix-length
417
418 proto_init_update "$ifname" 1
419 proto_set_keep 1
420 proto_add_ipv6_address "$ip_6" "128"
421 proto_add_ipv6_prefix "${ip_6}/${ip_prefix_length}"
422 proto_add_ipv6_route "$gateway_6" "128"
423 [ "$defaultroute" = 0 ] || proto_add_ipv6_route "::0" 0 "$gateway_6" "" "" "${ip_6}/${ip_prefix_length}"
424 [ "$peerdns" = 0 ] || {
425 proto_add_dns_server "$dns1_6"
426 proto_add_dns_server "$dns2_6"
427 }
428 [ -n "$zone" ] && {
429 proto_add_data
430 json_add_string zone "$zone"
431 proto_close_data
432 }
433 proto_send_update "$interface"
434 else
435 json_init
436 json_add_string name "${interface}_6"
437 json_add_string ifname "@$interface"
438 [ "$pdptype" = "ipv4v6" ] && json_add_string iface_464xlat "0"
439 json_add_string proto "dhcpv6"
440 [ -n "$ip6table" ] && json_add_string ip6table "$ip6table"
441 proto_add_dynamic_defaults
442 # RFC 7278: Extend an IPv6 /64 Prefix to LAN
443 json_add_string extendprefix 1
444 [ -n "$zone" ] && json_add_string zone "$zone"
445 json_close_object
446 ubus call network add_dynamic "$(json_dump)"
447 fi
448 }
449
450 [ -n "$pdh_4" ] && {
451 if [ "$dhcp" = 0 ]; then
452 json_load "$(uqmi -s -d $device -t 1000 --set-client-id wds,$cid_4 --get-current-settings)"
453 json_select ipv4
454 json_get_var ip_4 ip
455 json_get_var gateway_4 gateway
456 json_get_var dns1_4 dns1
457 json_get_var dns2_4 dns2
458 json_get_var subnet_4 subnet
459
460 proto_init_update "$ifname" 1
461 proto_set_keep 1
462 proto_add_ipv4_address "$ip_4" "$subnet_4"
463 proto_add_ipv4_route "$gateway_4" "128"
464 [ "$defaultroute" = 0 ] || proto_add_ipv4_route "0.0.0.0" 0 "$gateway_4"
465 [ "$peerdns" = 0 ] || {
466 proto_add_dns_server "$dns1_4"
467 proto_add_dns_server "$dns2_4"
468 }
469 [ -n "$zone" ] && {
470 proto_add_data
471 json_add_string zone "$zone"
472 proto_close_data
473 }
474 proto_send_update "$interface"
475 else
476 json_init
477 json_add_string name "${interface}_4"
478 json_add_string ifname "@$interface"
479 json_add_string proto "dhcp"
480 [ -n "$ip4table" ] && json_add_string ip4table "$ip4table"
481 proto_add_dynamic_defaults
482 [ -n "$zone" ] && json_add_string zone "$zone"
483 json_close_object
484 ubus call network add_dynamic "$(json_dump)"
485 fi
486 }
487 }
488
489 qmi_wds_stop() {
490 local cid="$1"
491 local pdh="$2"
492
493 [ -n "$cid" ] || return
494
495 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid" \
496 --stop-network 0xffffffff \
497 --autoconnect > /dev/null 2>&1
498
499 [ -n "$pdh" ] && {
500 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid" \
501 --stop-network "$pdh" > /dev/null 2>&1
502 }
503
504 uqmi -s -d "$device" -t 1000 --set-client-id wds,"$cid" \
505 --release-client-id wds > /dev/null 2>&1
506 }
507
508 proto_qmi_teardown() {
509 local interface="$1"
510
511 local device cid_4 pdh_4 cid_6 pdh_6
512 json_get_vars device
513
514 [ -n "$ctl_device" ] && device=$ctl_device
515
516 echo "Stopping network $interface"
517
518 json_load "$(ubus call network.interface.$interface status)"
519 json_select data
520 json_get_vars cid_4 pdh_4 cid_6 pdh_6
521
522 qmi_wds_stop "$cid_4" "$pdh_4"
523 qmi_wds_stop "$cid_6" "$pdh_6"
524
525 proto_init_update "*" 0
526 proto_send_update "$interface"
527 }
528
529 [ -n "$INCLUDE_ONLY" ] || {
530 add_protocol qmi
531 }