meta-netmodule-bsp/recipes-bsp/mac-address-set/files/mac-address-set.sh

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 $?