From 738152176d3c0e3713e0629755783839b4ceed27 Mon Sep 17 00:00:00 2001 From: Thilo-Alexander Ginkel Date: Tue, 7 Nov 2023 00:36:52 +0100 Subject: fcc-unlock: add support for Lenovo-shipped FM350-GL FCC unlock --- data/dispatcher-fcc-unlock/14c3 | 92 ++++++++++++++++++++++++++++++++++ data/dispatcher-fcc-unlock/meson.build | 2 + 2 files changed, 94 insertions(+) create mode 100755 data/dispatcher-fcc-unlock/14c3 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 +# +# 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', } -- cgit v1.2.3-70-g09d2