# # Cellular configuration for Komashi. # # vim: et:ts=2:sw=2: # { pkgs, deprekages, ... }: { config = { # # Set up ModemManager to FCC unlock this device. # environment.etc."ModemManager/fcc-unlock.d/8086:7560" = { text = '' #!${pkgs.bash}/bin/bash # SPDX-License-Identifier: CC0-1.0 # 2024 Stoica Floris # # Lenovo-shipped XMM7560 (8086:7560) FCC unlock # Ensure we can touch coreutils. PATH=$PATH:${pkgs.coreutils}/bin 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-xmm7560-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 ${pkgs.gnugrep}/bin/grep -q AT "/sys/class/wwan/$PORT/type" 2>/dev/null && { AT_PORT=$PORT break } # match port name in Linux 5.13 echo "$PORT" | ${pkgs.gnugrep}/bin/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 } reverseWithLittleEndian() { num="$1" printf "%x" $(("0x''${num:6:2}''${num:4:2}''${num:2:2}''${num:0:2}")) } VENDOR_ID_HASH="3df8c719" for i in {1..9}; do sleep 2 log "Requesting challenge from WWAN modem (attempt #''${i})" RAW_CHALLENGE=$(at_command "at+gtfcclockgen") CHALLENGE=$(echo "$RAW_CHALLENGE" | ${pkgs.gnugrep}/bin/grep -o '0x[0-9a-fA-F]\+' | ${pkgs.gawk}/bin/awk '{print $1}') if [ -n "$CHALLENGE" ]; then log "Got challenge from modem: $CHALLENGE" HEX_CHALLENGE=$(printf "%08x" "$CHALLENGE") REVERSE_HEX_CHALLENGE=$(reverseWithLittleEndian "''${HEX_CHALLENGE}") COMBINED_CHALLENGE="''${REVERSE_HEX_CHALLENGE}''${VENDOR_ID_HASH}" RESPONSE_HASH=$(printf "%s" "$COMBINED_CHALLENGE" | ${pkgs.xxd}/bin/xxd -r -p | sha256sum | cut -d ' ' -f 1) TRUNCATED_RESPONSE=$(printf "%.8s" "''${RESPONSE_HASH}") REVERSED_RESPONSE=$(reverseWithLittleEndian "$TRUNCATED_RESPONSE") RESPONSE=$(printf "%d" "0x''${REVERSED_RESPONSE}") log "Sending hash modem: $RESPONSE" UNLOCK_RESPONSE=$(at_command "at+gtfcclockver=$RESPONSE") log "at+gtfcclockver response = $UNLOCK_RESPONSE" UNLOCK_RESPONSE=$(at_command "at+gtfcclockmodeunlock") log "at+gtfcclockmodeunlock response = $UNLOCK_RESPONSE" UNLOCK_RESPONSE=$(at_command "at+cfun=1") log "at+cfun response = $UNLOCK_RESPONSE" UNLOCK_RESPONSE=$(at_command "at+gtfcclockstate") log "at+gtfcclockstate response = $UNLOCK_RESPONSE" UNLOCK_RESPONSE=$(echo "$UNLOCK_RESPONSE" | tr -d '\r') at_command "at+xdns=0,1" sleep 1 if [ "$UNLOCK_RESPONSE" = "1" ]; then log "FCC was unlocked previously" exit 0 fi if [ "$UNLOCK_RESPONSE" = "OK" ]; then log "FCC unlock success" exit 0 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 ''; # Let anyone execute this. mode = "555"; }; }; }