140 lines
4 KiB
Nix
140 lines
4 KiB
Nix
#
|
|
# 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 <floris@nusunt.eu>
|
|
#
|
|
# 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";
|
|
};
|
|
};
|
|
|
|
}
|