#!/bin/sh

NVMEM_PATH="/sys/bus/nvmem/devices/imx-ocotp0/nvmem"

check_nvmem_path () {
  if [ ! -r ${NVMEM_PATH} ]; then
    logger -s "${me} nvmem not readable..."
    exit 1
  fi
}

get_ocotp_offset () {
  ocotp_bank=$1
  ocotp_word=$2
  ocotp_offset=$(expr $ocotp_bank \* 8 + $ocotp_word)
  echo "${ocotp_offset}"
}

# reads one 32bit word from IMX OTP at offset $1
# Note: The line used here to retrieve the ASCII value puts lots of spaces behind
#       the value. The echo line cuts them away.
# Note: The found-on-the-web-style with 'of=/dev/stdout' operand in
#       the dd statement fails with gds-devices builds (for whatever reason).
#       As per dd man page 'of' is to "write to FILE instead of stdout". So
#       we just omit 'of' as we want to write to stdout. That seems to work.
read_otp_word () {
  check_nvmem_path

  local the_read_word
  the_read_word=$(dd status=none if=${NVMEM_PATH} bs=4 count=1 skip=$1 | hexdump -v -e '4/4 "%08X " "\n"')
  echo "${the_read_word}" | cut -c 1-8
}

read_otp_single_value () {
  check_nvmem_path

  local otp_0=$(read_otp_word $(get_ocotp_offset $1 $2))
  echo "${otp_0}"
}

read_otp_double_value () {
  check_nvmem_path

  local otp_0=$(read_otp_word $(get_ocotp_offset $1 $2))
  local otp_1=$(read_otp_word $(get_ocotp_offset $1 $3))
  local otp_val
  otp_val=$(printf "%s%s\\n" "$(printf "%s" "${otp_1}" | cut -c 5-8)" "${otp_0}")
  echo "${otp_val}"
}

test_and_correct_imput_value () {
  local input_val=$1

  # Remove 0x | 0X from hexadecimal input
  input_val=$(echo "${input_val}" |  sed 's/0x//g')

  # Make sure no input is geater than 16 and not empty
  if [ "${#input_val}" -gt 16 ]; then
    input_val="ERROR"
  elif [ "${#input_val}" -eq 0 ]; then
    input_val="ERROR"
  fi

  # make sure input is a proper hexadecimal input
  if [ -z $(echo "$input_val" | grep "^[0-9a-fA-F]*$") ]; then                
    input_val="ERROR"
  fi   
  echo "${input_val}"
}

write_otp_single_value () {
  check_nvmem_path
  local corrected_val=$(test_and_correct_imput_value $3)
  if ! [ "${corrected_val}" = "ERROR" ]; then
    local otp0_le=$(get_little_endian "${corrected_val}")
    local otp0_offset=$(get_ocotp_offset $1 $2)
     printf "${otp0_le}" | dd of=${NVMEM_PATH} bs=4 count=1 seek=$otp0_offset conv=notrunc
  else
    logger -s "${me} ERROR cant process value $3"
    exit 1
  fi
}

write_otp_double_value () {
  check_nvmem_path
  local corrected_val=$(test_and_correct_imput_value $4)
  if ! [ "${corrected_val}" = "ERROR" ]; then
    local size_of_val=${#corrected_val}
    local otp0=$(echo "${corrected_val}" | cut -c $((${size_of_val}-7))-)
    local otp1=$(echo "${corrected_val}" | cut -c 1-$((${size_of_val}-8)))
    local otp0_le=$(get_little_endian "${otp0}")
    local otp1_le=$(get_little_endian "${otp1}")
    local otp0_offset=$(get_ocotp_offset $1 $2)
    local otp1_offset=$(get_ocotp_offset $1 $3)
    printf "${otp0_le}" | dd of=${NVMEM_PATH} bs=4 count=1 seek=$otp0_offset conv=notrunc
    printf "${otp1_le}" | dd of=${NVMEM_PATH} bs=4 count=1 seek=$otp1_offset conv=notrunc
  else                                                                           
    logger -s "${me}ERROR cant process value $4"
    exit 1                                        
  fi
}

get_little_endian () {
  local input_val=$1
  local result=""
  local tmp=""
  local i=$((${#input_val}-2)) 
  while [ "$i" -ge 0 ]; do
    let loops++
    tmp=$(printf "%s" "${input_val:$i:2}")
    result=${result}\\x${tmp}
    i=$((i-=2))
  done 
  local rest=$(($loops%4))
  local j=0
  while [ "$j" -lt "$rest" ]; do
    result=${result}\\x00
    j=$((j+1))
  done
  echo "${result}"
}

