379 lines
7.4 KiB
Bash
Executable File
379 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
help_text='
|
|
This script configures bluetooth and wifi mac addresses
|
|
It is using the eth0 mac address as reference and then applying
|
|
a logical operation based on the mac address range.
|
|
|
|
Since the handling of wifi and bluetooth devices may be different
|
|
on different devices, this script is doing several attempts with
|
|
different approaches to be compatible with all devices.
|
|
|
|
Currently supported devices: HW21 and HW23
|
|
|
|
It currently supports two schemes:
|
|
|
|
* VCU1 scheme, starts with 7C:97:63: and ends with:
|
|
eth0: :50:XX:YY
|
|
wlan0: :70:XX:YY
|
|
bt0: :80:XX:YY
|
|
|
|
* VCU2 scheme, starts with 00:11:2B: and ends with:
|
|
eth0: :XX:YY:ZZ
|
|
wlan0: :XX:YY:ZZ+3
|
|
bt0: :XX:YY:ZZ+4
|
|
|
|
This script is NOT setting eth0 mac address, this must be done
|
|
before (usually bootloader).
|
|
'
|
|
|
|
level_error=0
|
|
level_warning=1
|
|
level_info=2
|
|
level_test=3
|
|
|
|
log_level="$level_info"
|
|
|
|
eth_mac=0
|
|
set_wlan=0
|
|
set_bt=0
|
|
invert_bt_mac=0
|
|
|
|
log () {
|
|
msg_level="$1"
|
|
level_string="$2"
|
|
msg="$3"
|
|
|
|
if [ "$log_level" = "$level_test" ]; then
|
|
echo "$msg"
|
|
elif [ "$log_level" -ge "$msg_level" ]; then
|
|
echo "$0: $level_string: $msg"
|
|
fi
|
|
}
|
|
|
|
log_test() {
|
|
msg="$1"
|
|
log "$level_test" "test" "$msg"
|
|
}
|
|
|
|
log_info() {
|
|
msg="$1"
|
|
log "$level_info" "info" "$msg"
|
|
}
|
|
|
|
log_warning() {
|
|
msg="$1"
|
|
log "$level_warning" "warning" "$msg"
|
|
}
|
|
|
|
log_error() {
|
|
msg="$1"
|
|
log "$level_error" "error" "$msg"
|
|
}
|
|
|
|
wait_on() {
|
|
cmd=$1
|
|
device=$2
|
|
if $cmd "$device" > /dev/null 2>/dev/null; then
|
|
return
|
|
fi
|
|
log_info "$device not ready"
|
|
log_info "Waiting...."
|
|
wait_timeout=0
|
|
while ! $cmd "$device" > /dev/null 2>/dev/null; do
|
|
if [ $wait_timeout -gt 10 ]; then
|
|
log_warning "Timeout after 10 seconds, exiting now.."
|
|
break
|
|
else
|
|
sleep 1
|
|
fi
|
|
wait_timeout=$(($wait_timeout+1))
|
|
done
|
|
log_info "$device ready"
|
|
}
|
|
|
|
wait_on_wlan0() {
|
|
wait_on ifconfig wlan0
|
|
}
|
|
|
|
wait_on_hci0() {
|
|
wait_on hciconfig hci0
|
|
}
|
|
|
|
is_wlan0_up() {
|
|
if ifconfig wlan0 | grep UP > /dev/null;
|
|
then
|
|
return "1"
|
|
else
|
|
return "0"
|
|
fi
|
|
}
|
|
|
|
set_wlan_mac_address() {
|
|
address=$1
|
|
log_info "Setting wlan0 mac address to $address"
|
|
ip link set wlan0 address "$address"
|
|
ret=$?
|
|
if [ $ret -ne 0 ]; then
|
|
log_warning "Setting wlan address failed $ret"
|
|
fi
|
|
return $ret
|
|
}
|
|
|
|
set_and_check_wlan_mac_address() {
|
|
address=$1
|
|
|
|
set_wlan_mac_address "$address"
|
|
ret=$?
|
|
|
|
if [ $ret -ne 0 ]; then
|
|
log_warning "Setting wlan address failed: $ret"
|
|
return $ret
|
|
fi
|
|
|
|
log_info "Checking success"
|
|
# Setting the mac may fail without error
|
|
# Checking MAC to be sure it worked
|
|
if ifconfig wlan0 | grep -i "$address" > /dev/null; then
|
|
log_info "wlan mac address successfully set"
|
|
return 0
|
|
else
|
|
log_warning "Setting wlan address failed without throwing an error"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
prepare_wlan() {
|
|
is_wlan0_up
|
|
wlan_iface_was_up=$?
|
|
|
|
if [[ "$wlan_iface_was_up" -eq "1" ]]; then
|
|
log_info "Taking wlan0 down"
|
|
ifconfig wlan0 down
|
|
fi
|
|
return "$wlan_iface_was_up"
|
|
}
|
|
|
|
unprepare_wlan() {
|
|
wlan_iface_was_up=$1
|
|
|
|
if [[ "$wlan_iface_was_up" -eq "1" ]]; then
|
|
log_info "Taking wlan0 back up"
|
|
ifconfig wlan0 up
|
|
else
|
|
log_info "Making sure wlan0 is down"
|
|
ifconfig wlan0 down
|
|
fi
|
|
}
|
|
|
|
prepare_and_set_wlan_mac_address() {
|
|
address="$1"
|
|
|
|
wait_on_wlan0
|
|
prepare_wlan
|
|
wlan_iface_was_up=$?
|
|
|
|
set_and_check_wlan_mac_address "$address"
|
|
ret=$?
|
|
if [[ "$ret" -ne 0 ]]; then
|
|
# On some devices the interface must be up to set the address
|
|
log_warning "Setting wlan address failed, trying with interface up"
|
|
ifconfig wlan0 up
|
|
set_and_check_wlan_mac_address "$address"
|
|
ret=$?
|
|
fi
|
|
|
|
unprepare_wlan "$wlan_iface_was_up"
|
|
|
|
return $ret
|
|
}
|
|
|
|
|
|
# Returns 1 in case reset failed
|
|
# Returns 2 when the address is not correct after reset
|
|
set_and_check_bt_mac_address() {
|
|
address="$1"
|
|
addr_to_set="$address" # Because of some bug the address to set may be inverted
|
|
if [[ "$invert_bt_mac" -eq "1" ]]; then
|
|
addr_to_set=$(echo "$address" | sed -r "s/^(.{2}):(.{2}):(.{2}):(.{2}):(.{2}):(.{2})/\\6:\\5:\\4:\\3:\\2:\\1/")
|
|
fi
|
|
|
|
bdaddr "$addr_to_set" > /dev/null
|
|
ret=$?
|
|
if [ $ret -ne 0 ]; then
|
|
log_warning "Setting bluetooth address failed: $ret"
|
|
fi
|
|
|
|
sleep 2
|
|
log_info "Restarting bluetooth service"
|
|
if ! hciconfig hci0 reset; then
|
|
return 1
|
|
fi
|
|
|
|
systemctl restart bluetooth
|
|
sleep 1
|
|
wait_on_hci0
|
|
hciconfig hci0 up
|
|
|
|
log_info "Checking success"
|
|
if hciconfig hci0 | grep -i "$address" > /dev/null; then
|
|
log_info "Bluetooth address successfully set"
|
|
ret=0
|
|
else
|
|
log_warning "Setting bluetooth address failed without error"
|
|
ret=2
|
|
fi
|
|
return $ret
|
|
}
|
|
|
|
prepare_and_set_bt_mac_address() {
|
|
address=$1
|
|
|
|
wait_on_hci0
|
|
|
|
hciconfig hci0 up
|
|
|
|
log_info "Setting bluetooth mac address to $address"
|
|
set_and_check_bt_mac_address "$address"
|
|
ret=$?
|
|
|
|
return $ret
|
|
}
|
|
|
|
do_bt_hard_reset() {
|
|
if systemctl is-active --quiet jody-w1-bt-init; then
|
|
systemctl restart jody-w1-bt-init
|
|
elif systemctl is-active --quiet tibluetooth; then
|
|
systemctl restart tibluetooth
|
|
else
|
|
log_error "No hard reset possible"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# VCU1 scheme, starts with 7C:97:63:
|
|
# eth0: 50:XX:YY
|
|
# wlan0: 70:XX:YY
|
|
# bt0: 80:XX:YY
|
|
is_vcu1_mac() {
|
|
[[ ${1^^} == "7C:97:63:50:"* ]]
|
|
}
|
|
|
|
vcu1_eth_to_wlan() {
|
|
echo "${1^^}" | sed -r "s/^(.{9})50(.{6})/\\170\\2/"
|
|
}
|
|
|
|
vcu1_eth_to_bt() {
|
|
echo "${1^^}" | sed -r "s/^(.{9})50(.{6})/\\180\\2/"
|
|
}
|
|
|
|
# VCU2 scheme, starts with 00:11:2B:
|
|
# eth0: XX:YY:ZZ
|
|
# wlan0: XX:YY:ZZ+3
|
|
# bt0: XX:YY:ZZ+4
|
|
is_vcu2_mac() {
|
|
[[ $1 == "00:11:2B:"* ]]
|
|
}
|
|
|
|
vcu2_eth_to_wlan_and_bt() {
|
|
iface="$1"
|
|
IFS=':' read -r -a digits <<< "$2"
|
|
|
|
# convert digits from hex to decimal
|
|
for ((x=0 ; x<6 ; x++)); do digits[x]=0x${digits[x]}; done
|
|
|
|
if [ $iface = "wlan" ]; then
|
|
digits[5]=$((digits[5] + 3))
|
|
elif [ $iface = "bt" ]; then
|
|
digits[5]=$((digits[5] + 4))
|
|
fi
|
|
|
|
mac=$(printf ":%02X" "${digits[@]}")
|
|
mac=${mac:1} # Remove first ":"
|
|
|
|
echo "$mac"
|
|
}
|
|
|
|
main() {
|
|
ret=0
|
|
|
|
if [ "$1" = "-h" ]; then
|
|
echo "$help_text"
|
|
echo "Usage:"
|
|
echo " $0"
|
|
echo " $0 -l <loglevel> # loglevel being between 0 (errors) and 2 (info)"
|
|
echo " $0 -w # set wifi address"
|
|
echo " $0 -b # set bluetooth address"
|
|
echo " $0 -i # invert bluetooth address"
|
|
echo " $0 -e <eth0 addr> # mac address input, e.g. 00:11:22:33:44:10"
|
|
exit 0
|
|
fi
|
|
|
|
while getopts l:wbie: option
|
|
do
|
|
case "${option}"
|
|
in
|
|
l)log_level=${OPTARG};;
|
|
w)set_wlan=1;;
|
|
b)set_bt=1;;
|
|
i)invert_bt_mac=1;;
|
|
e)eth_mac=${OPTARG};;
|
|
esac
|
|
done
|
|
|
|
if [ $eth_mac = 0 ]; then
|
|
eth_mac=$(ip addr show eth0 | awk '$1 == "link/ether" {gsub(/\/.*$/, "", $2); print $2}')
|
|
fi
|
|
eth_mac=${eth_mac^^} # UPPER case
|
|
if is_vcu1_mac "$eth_mac"; then
|
|
log_info "VCU1 mac address scheme"
|
|
wlan_mac=$(vcu1_eth_to_wlan "$eth_mac")
|
|
bt_addr=$(vcu1_eth_to_bt "$eth_mac")
|
|
elif is_vcu2_mac "$eth_mac"; then
|
|
log_info "VCU2 mac address scheme"
|
|
wlan_mac=$(vcu2_eth_to_wlan_and_bt "wlan" "$eth_mac")
|
|
bt_addr=$(vcu2_eth_to_wlan_and_bt "bt" "$eth_mac")
|
|
else
|
|
log_error "Unknown mac address scheme for: $eth_mac"
|
|
exit 1
|
|
fi
|
|
|
|
log_test "wlan: $wlan_mac"
|
|
log_test "bt: $bt_addr"
|
|
|
|
if [ $set_wlan = 1 ]; then
|
|
prepare_and_set_wlan_mac_address "$wlan_mac"
|
|
ret_wlan=$?
|
|
if [ $ret_wlan -ne 0 ]; then
|
|
log_error "Failed to set wlan mac address"
|
|
ret="$ret_wlan"
|
|
fi
|
|
fi
|
|
|
|
if [ $set_bt = 1 ]; then
|
|
prepare_and_set_bt_mac_address "$bt_addr"
|
|
ret_bt=$?
|
|
|
|
while [ $ret_bt -eq 1 ]; do
|
|
log_warning "Soft reset failed, doing hard reset and retrying"
|
|
do_bt_hard_reset
|
|
prepare_and_set_bt_mac_address "$bt_addr"
|
|
ret_bt=$?
|
|
done
|
|
|
|
if [ $ret_bt -ne 0 ]; then
|
|
log_error "Failed to set bluetooth mac address"
|
|
ret="$ret_bt"
|
|
fi
|
|
fi
|
|
|
|
return "$ret"
|
|
}
|
|
|
|
if [ "$1" = "testify" ]; then
|
|
return 0
|
|
fi
|
|
|
|
main $@
|
|
exit $?
|