aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdata/dispatcher-fcc-unlock/14c392
-rw-r--r--data/dispatcher-fcc-unlock/meson.build2
2 files changed, 94 insertions, 0 deletions
diff --git a/data/dispatcher-fcc-unlock/14c3 b/data/dispatcher-fcc-unlock/14c3
new file mode 100755
index 00000000..51217f4d
--- /dev/null
+++ b/data/dispatcher-fcc-unlock/14c3
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: CC0-1.0
+# 2023 Thilo-Alexander Ginkel <thilo@ginkel.com>
+#
+# Lenovo-shipped Fibocom FM350-GL (14c3:4d75) FCC unlock
+
+if [[ "$FCC_UNLOCK_DEBUG_LOG" == '1' ]]; then
+ exec 3>&1 4>&2
+ trap 'exec 2>&4 1>&3' 0 1 2 3
+ exec 1>>/var/log/mm-fm350-fcc.log 2>&1
+fi
+
+# require program name and at least 2 arguments
+[ $# -lt 2 ] && exit 1
+
+# first argument is DBus path, not needed here
+shift
+
+# second and next arguments are control port names
+for PORT in "$@"; do
+ # match port type in Linux 5.14 and newer
+ grep -q AT "/sys/class/wwan/$PORT/type" 2>/dev/null && {
+ AT_PORT=$PORT
+ break
+ }
+ # match port name in Linux 5.13
+ echo "$PORT" | grep -q AT && {
+ AT_PORT=$PORT
+ break
+ }
+done
+
+# fail if no AT port exposed
+[ -n "$AT_PORT" ] || exit 2
+
+DEVICE=/dev/${AT_PORT}
+
+at_command() {
+ exec 99<>"$DEVICE"
+ echo -e "$1\r" >&99
+ read answer <&99
+ read answer <&99
+ echo "$answer"
+ exec 99>&-
+}
+
+log() {
+ echo "$1"
+}
+
+error() {
+ echo "$1" >&2
+}
+
+VENDOR_ID_HASH="3df8c719"
+
+for i in {1..9}; do
+ log "Requesting challenge from WWAN modem (attempt #${i})"
+ RAW_CHALLENGE=$(at_command "at+gtfcclockgen")
+ CHALLENGE=$(echo "$RAW_CHALLENGE" | grep -o '0x[0-9a-fA-F]\+' | awk '{print $1}')
+
+ if [ -n "$CHALLENGE" ]; then
+ log "Got challenge from modem: $CHALLENGE"
+ HEX_CHALLENGE=$(printf "%08x" "$CHALLENGE")
+ COMBINED_CHALLENGE="${HEX_CHALLENGE}$(printf "%.8s" "${VENDOR_ID_HASH}")"
+ RESPONSE_HASH=$(echo "$COMBINED_CHALLENGE" | xxd -r -p | sha256sum | cut -d ' ' -f 1)
+ TRUNCATED_RESPONSE=$(printf "%.8s" "$RESPONSE_HASH")
+ RESPONSE=$(printf "%d" "0x$TRUNCATED_RESPONSE")
+
+ log "Sending response to WWAN modem: $RESPONSE"
+ UNLOCK_RESPONSE=$(at_command "at+gtfcclockver=$RESPONSE")
+
+ if [[ "$UNLOCK_RESPONSE" == "+GTFCCLOCKVER:"* ]]; then
+ UNLOCK_RESULT=$(echo "$UNLOCK_RESPONSE" | grep -o '[0-9]\+')
+ if [[ "$UNLOCK_RESULT" == "1" ]]; then
+ log "FCC unlock succeeded"
+ exit 0
+ else
+ error "FCC unlock failed. Got result: $UNLOCK_RESULT"
+ fi
+ else
+ error "Unlock failed. Got response: $UNLOCK_RESPONSE"
+ fi
+ else
+ error "Failed to obtain FCC challenge. Got: ${RAW_CHALLENGE}"
+ fi
+
+ sleep 0.5
+done
+
+exit 2
diff --git a/data/dispatcher-fcc-unlock/meson.build b/data/dispatcher-fcc-unlock/meson.build
index b2a049e9..aa8d1ba0 100644
--- a/data/dispatcher-fcc-unlock/meson.build
+++ b/data/dispatcher-fcc-unlock/meson.build
@@ -13,6 +13,7 @@ mm_fccunlockdirpackage = mm_pkglibdir / 'fcc-unlock.d'
examples = files(
'105b',
'1199',
+ '14c3',
'1eac',
'2c7c',
)
@@ -30,6 +31,7 @@ vidpids = {
'1199:9079': '1199',
'413c:81a3': '1199',
'413c:81a8': '1199',
+ '14c3:4d75': '14c3',
'1eac:1001': '1eac',
'2c7c:030a': '2c7c',
}