#!/bin/bash
#
#  configure
#  Shell script to configure the NI-KAL kernel build, creates Makefile.in
#  This script must be run before compiling the NI-KAL kernel module
#
#   Copyright 2003-2005,
#  National Instruments Corporation.
#  All Rights reserved.
#
#  originated:  17 March 2003
#

statusFail=1
statusSuccess=0

__SELF__=$(if [ -L "$0" ]; then readlink "$0"; else echo "$0"; fi;)
__DIR__="$(cd "$(dirname "${__SELF__}")" && pwd)"

# Configure logging
LOG_MSG_TAG="nikal"

# Get useful functions to help with install
if [[ -e /usr/share/nikal/installerUtility.sh ]]; then
   source /usr/share/nikal/installerUtility.sh
elif [[ -e /usr/local/natinst/nikal/bin/installerUtility.sh ]]; then
   source /usr/local/natinst/nikal/bin/installerUtility.sh
elif [[ -e ${__DIR__}/../../bin/installerUtility.sh ]]; then
   source ${__DIR__}/../../bin/installerUtility.sh
elif [[ -e ${__DIR__}/installerUtility.sh ]]; then
   source ${__DIR__}/installerUtility.sh
else
   echo "Error opening the NI-KAL driver utility library." >&2
   exit 1
fi

# Set local to "C" or Posix to make grep -w work
export LC_ALL=C

if ! nikalInstallationCheck; then
   exit $statusFail
fi

info " "
info "Configuring for linux kernel version $kernelVersion."
info " "

niModulePath=$INSTALL_MOD_PATH/lib/modules/$kernelVersion/kernel/natinst

info "Using kernel headers found in $headersDir."
info "If this does not correspond to the location of the $kernelVersion headers,"
info "then define KERNELHEADERS in your environment to point to the location"
info "of the kernel headers, define KERNELTARGET as the version of the"
info "kernel for which to compile, and then rerun $0."
info " "

if ! requirecommands cat grep sed awk wc cut expr; then
   error "Some required tools are missing or were not found."
   return $statusFail
fi

### Determining kernel version code ###
if [ -e $headersDir/include/generated/uapi/linux/version.h ]; then
   LINUX_VERSION_CODE=$(cat $headersDir/include/generated/uapi/linux/version.h | grep LINUX_VERSION_CODE | cut -d " " -f 3)
   VERSION_DIR=$headersDir/include/generated/uapi/linux/version.h
elif [ -e $headersDir/include/linux/version.h ]; then
   LINUX_VERSION_CODE=$(cat $headersDir/include/linux/version.h | grep LINUX_VERSION_CODE | cut -d " " -f 3)
   VERSION_DIR=$headersDir/include/linux/version.h
elif [ -e $headersDir/Makefile ]; then
   LINUX_VERSION=$(cat $headersDir/Makefile | grep "^VERSION" | sed 's/.*= *//')
   LINUX_PATCHLEVEL=$(cat $headersDir/Makefile | grep "^PATCHLEVEL" | sed 's/.*= *//')
   LINUX_SUBLEVEL=$(cat $headersDir/Makefile | grep "^SUBLEVEL" | sed 's/.*= *//')
   LINUX_VERSION_CODE=$(expr $LINUX_VERSION * 65536 + $LINUX_PATCHLEVEL * 256 + $LINUX_SUBLEVEL )
else
   LINUX_VERSION_CODE=$(expr $kernelMajor * 65536 + $kernelMinor * 256 + $kernelPatchLevel )
fi
LINUX_260_VERSION_CODE=132608
LINUX_270_VERSION_CODE=132864

### Determining kernel variant ###
reply=

VERSION_VARIANT=""
if [ "$VERSION_DIR" = "$headersDir/include/generated/uapi/linux/version.h" ]; then
   reply="yes"
   VERSION_VARIANT="-DnNIKAL250_kUAPIVersion"
else
   reply="no"
fi
info "Kernel has version.h in include/generated/uapi/: $reply"
reply=

REPARENT_VARIANT=""
reparent_to_init=$(grep reparent_to_init $headersDir/kernel/* 2>/dev/null | grep -wc reparent_to_init)
if [ $reparent_to_init -ne 0 ]; then
   reply="yes"
   REPARENT_VARIANT="-DnNIKAL100_kReparentToInit"
else
   reply="no"
fi
info "Kernel has reparent_to_init(): $reply"
reply=

MUNMAP_VARIANT=""
munmap_fourth=$(grep do_munmap $headersDir/include/linux/mm.h | grep -wc "int acct")
if [ $munmap_fourth -eq 1 ]; then
   reply="4"
   MUNMAP_VARIANT="-DnNIKAL100_kFourParameterDoMunmap"
else
   reply="3"
fi
info "Number of arguments for do_munmap(): $reply"
reply=

VMMMAP_VARIANT=""
vm_mmap=$(grep -wc vm_mmap $headersDir/include/linux/mm.h)
if [ $vm_mmap -ne 0 ]; then
   reply="yes"
   VMMMAP_VARIANT="-DnNIKAL240_kHasVmMmap"
else
   reply="no"
fi
info "Kernel has vm_mmap(): $reply"
reply=

VMMUNMAP_VARIANT=""
vm_munmap=$(grep -wc vm_munmap $headersDir/include/linux/mm.h)
if [ $vm_munmap -ne 0 ]; then
   reply="yes"
   VMMUNMAP_VARIANT="-DnNIKAL240_kHasVmMunmap"
else
   reply="no"
fi
info "Kernel has vm_munmap(): $reply"
reply=

VM_RESERVED_VARIANT=""
vm_rsvd=$(grep -wc VM_RESERVED $headersDir/include/linux/mm.h)
if [ $vm_rsvd -ne 0 ]; then
   reply="yes"
   VM_RESERVED_VARIANT="-DnNIKAL240_kHasVM_RESERVED"
else
   reply="no"
fi
info "Kernel has VM_RESERVED: $reply"
reply=

BUSNRES_VARIANT=""
busnres=$(grep 'struct[[:space:]]\+resource' $headersDir/include/linux/pci.h | grep -wc 'busn_res')
if [ $busnres -ne 0 ]; then
   reply="yes"
   BUSNRES_VARIANT="-DnNIKAL240_kHasPciBusnRes"
else
   reply="no"
fi
info "Kernel struct pci_dev has busn_res member: $reply"
reply=

ACPIREMOVE_VARIANT=""
acpiremovetype=$(grep 'typedef.\+acpi_op_remove' $headersDir/include/acpi/acpi_bus.h | grep -wc 'type')
if [ "$acpiremovetype" -ne 0 ]; then
   reply="yes"
   ACPIREMOVE_VARIANT="-DnNIKAL1500_kACPIRemoveHasTypeInput"
else
   reply="no"
fi
info "Kernel acpi_op_remove callback has additional \"type\" parameter: $reply"
reply=

OFFSET_VARIANT=""
pte_offset_kernel_path=""
if [ -d $headersDir/arch/x86/include/asm ]; then
   pte_offset_kernel_path="$headersDir/arch/x86/include/asm/*"
elif [ -d $headersDir/arch/arm/include/asm ]; then
   pte_offset_kernel_path="$headersDir/arch/arm/include/asm/*"
fi
if [ -d $headersDir/include/asm ]; then
   pte_offset_kernel_path="$pte_offset_kernel_path $headersDir/include/asm/*"
fi
pte_offset_kernel=$(grep pte_offset_kernel $pte_offset_kernel_path 2>/dev/null | grep -wc pte_offset_kernel)
if [ $pte_offset_kernel -ne 0 ]; then
   reply="pte_offset_kernel()"
   OFFSET_VARIANT="-DnNIKAL100_kPTEOffsetKernel"
else
   reply="pte_offset()"
fi
info "pte_offset function: $reply"
reply=

PAGE_TABLE_LEVEL_VARIANT=""
if [ -e $headersDir/include/asm-generic ]; then
   pud_offset_exists=$(grep pud_offset $headersDir/include/asm-generic/* 2>/dev/null | grep -wc pud_offset)
   if [ $pud_offset_exists -ne 0 ]; then
      reply="4"
      PAGE_TABLE_LEVEL_VARIANT="-DnNIKAL100_kFourLevelPageTable"
   else
      reply="3"
   fi
else
   reply="3"
fi
info "Levels in page table: $reply"
reply=

KZALLOC_VARIANT=""
kzalloc=$(grep -wc kzalloc $headersDir/include/linux/slab.h)
if [ $kzalloc -ne 0 ]; then
   reply="yes"
   KZALLOC_VARIANT="-DnNIKAL200_kHasKzalloc"
else
   reply="no"
fi
info "Kernel has kzalloc: $reply"
reply=

CHAINED_SGL_VARIANT=""
chained_sgl=$(grep -wc sg_alloc_table $headersDir/include/linux/scatterlist.h)
if [ $chained_sgl -ne 0 ]; then
   # TODO: CAR 450729.  There's a better way to do this.
   if [ -d $headersDir/arch/arm/include/asm ]; then
      reply="limited"
      CHAINED_SGL_VARIANT="-DnNIKAL220_kHasSGLPageAPI"
   else
      reply="yes"
      CHAINED_SGL_VARIANT="-DnNIKAL220_kHasChainedSGL"
   fi
else
   reply="no"
fi
info "Kernel has chained SGL support: $reply"
reply=

USB_ALTSETTINGS_PTR_VARIANT=""
cur_altsetting=$(grep -wc cur_altsetting $headersDir/include/linux/usb.h)
if [ $cur_altsetting -ne 0 ]; then
   reply="cur_altsetting"
   USB_ALTSETTINGS_PTR_VARIANT="-DnNIKAL100_kCurrentAlternateSetting"
else
   reply="act_altsetting"
fi
info "USB altsetting name: $reply"
reply=

USB_INTERFACE_REFCOUNT_VARIANT=""
usb_get_intf=$(grep -wc usb_get_intf $headersDir/include/linux/usb.h)
if [ $usb_get_intf -ne 0 ]; then
   reply="yes"
   USB_INTERFACE_REFCOUNT_VARIANT="-DnNIKAL100_kUSBReferenceCountFunctions"
else
   reply="no"
fi
info "Kernel has usb_get_intf(): $reply"
reply=

USB_INTERFACE_CACHE_VARIANT=""
usb_interface_cache=$(awk '/struct usb_host_config \{/, /}/' $headersDir/include/linux/usb.h | grep -wc intf_cache)
if [ $usb_interface_cache -ne 0 ]; then
   reply="yes"
   USB_INTERFACE_CACHE_VARIANT="-DnNIKAL100_kUSBInterfaceCacheMember"
else
   reply="no"
fi
info "Kernel has intf_cache member in usb_host_config: $reply"
reply=

USB_DEVICE_EP_ARRAY_VARIANT=""
usb_device_ep_array=$(awk '/struct usb_device \{/, /}/' $headersDir/include/linux/usb.h | grep -wc ep_out)
if [ $usb_device_ep_array -ne 0 ]; then
   reply="yes"
   USB_DEVICE_EP_ARRAY_VARIANT="-DnNIKAL100_kUSBDeviceEndpointArray"
else
   reply="no"
fi
info "Kernel has ep[] members in usb_device: $reply"
reply=

USB_SET_CONFIGURATION_VARIANT=""
usb_set_configuration=$(grep -wc usb_set_configuration $headersDir/include/linux/usb.h)
if [ $usb_set_configuration -ne 0 ]; then
   reply="yes"
   USB_SET_CONFIGURATION_VARIANT="-DnNIKAL100_kUSBSetConfiguration"
else
   reply="no"
fi
info "Kernel exports usb_set_configuration(): $reply"
reply=

USB_TIMEOUT_UNITS_VARIANT=""
usb_timeout_units=$(grep -B 7 USB_CTRL_GET_TIMEOUT $headersDir/include/linux/usb.h | grep -wc milliseconds)
if [ $usb_timeout_units -ne 0 ]; then
   reply="msec"
   USB_TIMEOUT_UNITS_VARIANT="-DnNIKAL100_kUSBmsecTimeout"
else
   reply="sec"
fi
info "Units of USB_CTRL_GET_TIMEOUT: $reply"
reply=

USB_OWNER_VARIANT=""
usb_owner=$(awk '/struct usb_driver \{/, /}/' $headersDir/include/linux/usb.h | grep -wc owner)
if [ $usb_owner -ne 0 ]; then
   reply="yes"
   USB_OWNER_VARIANT="-DnNIKAL130_kUSBOwnerMember"
else
   reply="no"
fi
info "Kernel has owner member in usb_driver: $reply"
reply=

MUTEX_VARIANT=""
if [ -e $headersDir/include/linux/mutex.h ]; then
   reply="yes"
   MUTEX_VARIANT="-DnNIKAL150_kMutexMethod"
else
   reply="no"
fi
info "Kernel has mutex method: $reply"
reply=

COMPLETION_VARIANT=""
completion_interruptible=$(grep -wc wait_for_completion_interruptible $headersDir/include/linux/completion.h)
if [ $completion_interruptible -ne 0 ]; then
   reply="yes"
   COMPLETION_VARIANT="-DnNIKAL200_kCompletion"
else
   reply="no"
fi
info "Kernel has wait_for_completion_interruptible: $reply"
reply=

CONFIG_VARIANT=""
if [ -e $headersDir/include/linux/config.h ]; then
   reply="yes"
   CONFIG_VARIANT="-DnNIKAL160_kConfig"
else
   reply="no"
fi
info "Kernel has config.h: $reply"
reply=

IOCTL32_VARIANT=""
if [ -e $headersDir/include/linux/ioctl32.h ]; then
   reply="yes"
   IOCTL32_VARIANT="-DnNIKAL170_kIoctl32"
else
   reply="no"
fi
info "Kernel has ioctl32.h: $reply"
reply=

IRQ_REGS_VARIANT=""
pt_regs=$(grep -wc pt_regs $headersDir/include/linux/interrupt.h)
if [ $pt_regs -ne 0 ]; then
   reply="yes"
   IRQ_REGS_VARIANT="-DnNIKAL160_kIRQRegs"
else
   reply="no"
fi
info "IRQ handlers have pt_regs: $reply"
reply=

WORKQUEUE_VARIANT=""
work_struct_timer=$(awk '/struct work_struct \{/, /}/' $headersDir/include/linux/workqueue.h | grep -wc timer)
if [ $work_struct_timer -eq 0 ]; then
   reply="yes"
   WORKQUEUE_VARIANT="-DnNIKAL160_kWorkqueueNonDelay"
else
   reply="no"
fi
info "Kernel has work_struct and delayed_work: $reply"
reply=

IOREMAP_WC_VARIANT=""
ioremap_wc=$(grep -wc ioremap_wc $headersDir/include/asm-generic/iomap.h)
if [ $ioremap_wc -ne 0 ]; then
   reply="yes"
   IOREMAP_WC_VARIANT="-DnNIKAL230_kHas_ioremap_wc"
else
   reply="no"
fi
info "Kernel has ioremap_wc: $reply"
reply=

X86_CACHEMODE_PROTVAL_VARIANT=""
reply="no"
if [ -e $headersDir/arch/x86/include/asm/pgtable_types.h ]; then
   x86_cachemode_protval=$(grep -wc "unsigned long cachemode2protval" $headersDir/arch/x86/include/asm/pgtable_types.h)
   if [ $x86_cachemode_protval -ne 0 ]; then
      reply="yes"
      X86_CACHEMODE_PROTVAL_VARIANT="-DnNIKAL1500_kHasCachemode2ProtvalTranslation"
   fi
fi
info "Kernel has cachemode to protval translation for x86 page tables: $reply"
reply=

CRED_VARIANT=""
if [ -e $headersDir/include/linux/cred.h ]; then
   reply="yes"
   CRED_VARIANT="-DnNIKAL230_kHasCred"
else
   reply="no"
fi
info "Kernel has cred.h: $reply"
reply=

UIDGID_VARIANT=""
if [ -e $headersDir/include/linux/uidgid.h ]; then
   reply="yes"
   UIDGID_VARIANT="-DnNIKAL1400_kHasUidGid"
else
   reply="no"
fi
info "Kernel has uidgid.h: $reply"
reply=

UMH_VARIANT=""
umh_constants=$(grep -wc UMH_NO_WAIT $headersDir/include/linux/kmod.h)
if [ $umh_constants -ne 0 ]; then
   reply="yes"
   UMH_VARIANT="-DnNIKAL240_kHasUMHConstants"
else
   reply="no"
fi
info "Kernel has UMH constants: $reply"
reply=

HAS_CREATE_PROC_READ_ENTRY=""
proc_create_data=$(grep -wc create_proc_read_entry $headersDir/include/linux/proc_fs.h)
if [ $proc_create_data -ne 0 ]; then
   reply="yes"
   HAS_CREATE_PROC_READ_ENTRY="-DnNIKAL1400_kHasCreateProcReadEntry"
else
   reply="no"
fi
info "Kernel has create_proc_read_entry: $reply"
reply=

NAMESPACED_GENETLINK_VARIANT=""
namespaced_genetlink=$(awk '/genlmsg_unicast\(/, /\)/' $headersDir/include/net/genetlink.h | grep -c 'struct net')
if [ $namespaced_genetlink -ne 0 ]; then
   reply="yes"
   NAMESPACED_GENETLINK_VARIANT="-DnNIKAL250_kHasNamespacedGenetlink"
else
   reply="no"
fi
info "Kernel has namespaced generic netlink API: $reply"
reply=

GENLMSG_NEW_VARIANT=""
genlmsg_new=$(grep -wc genlmsg_new $headersDir/include/net/genetlink.h)
if [ $genlmsg_new -ne 0 ]; then
   reply="yes"
   GENLMSG_NEW_VARIANT="-DnNIKAL250_kHasGenlmsgNew"
else
   reply="no"
fi
info "Kernel has genlmsg_new: $reply"
reply=

FAMILY_GENLMSG_PUT_VARIANT=""
family_genlmsg_put=$(awk '/genlmsg_put\(/, /\)/' $headersDir/include/net/genetlink.h | grep -c 'struct genl_family')
if [ $family_genlmsg_put -ne 0 ]; then
   reply="yes"
   FAMILY_GENLMSG_PUT_VARIANT="-DnNIKAL250_kHasFamilyGenlmsgPut"
else
   reply="no"
fi
info "Kernel has family genlmsg_put: $reply"
reply=

FAMILY_GENLOPS_GROUPS_VARIANT=""
family_genlops_groups=$(grep -wc genl_register_family_with_ops_groups $headersDir/include/net/genetlink.h)
if [ $family_genlops_groups -ne 0 ]; then
   reply="yes"
   FAMILY_GENLOPS_GROUPS_VARIANT="-DnNIKAL1400_kHasFamilyGenlOpsGroups"
else
   reply="no"
fi
info "Kernel has family genl_ops_groups: $reply"
reply=

HIGHRES_TIMER_VARIANT=""
usleep_range=$(grep -wc usleep_range $headersDir/include/linux/delay.h)
if [ $usleep_range -ne 0 ]; then
   reply="yes"
   HIGHRES_TIMER_VARIANT="-DnNIKAL100_kHighresTimerAvailable"
else
   reply="no"
fi
info "Kernel has usleep_range(): $reply"
reply=

HAS_RS485_SUPPORT=""
txvr_ops=$(grep -wc txvr_ops $headersDir/include/linux/serial_core.h)
if [ $txvr_ops -ne 0 ]; then
   reply="yes"
   HAS_RS485_SUPPORT="-DnNIKAL100_kHasRS485Support"
else
   reply="no"
fi
info "Kernel has RS-485 serial_core support: $reply"
reply=

HAS_RS485_CONFIG_ON_UART=""
rs485_config_uart=$(awk '/struct uart_port \{/, /}/' $headersDir/include/linux/serial_core.h | grep -wc rs485_config)
if [ $rs485_config_uart -ne 0 ]; then
   reply="yes"
   HAS_RS485_CONFIG_ON_UART="-DnNIKAL1500_kHasRS485ConfigOnUart"
else
   reply="no"
fi
info "Kernel RS-485 config options are per-uart: $reply"
reply=

UART_SET_WAKE_VARIANT=""
uart_set_wake=$(grep -wc set_wake $headersDir/include/linux/serial_core.h)
if [ $uart_set_wake -ne 0 ]; then
   reply="yes"
   UART_SET_WAKE_VARIANT="-DnNIKAL1400_kHasUartSetWake"
else
   reply="no"
fi
info "Kernel struct uart_ops has set_wake(): $reply"
reply=

USE_TTY_PORT_PARAM=""
tty_port_param=$(grep -wc 'tty_insert_flip_char[[:blank:]]*([[:blank:]]*struct tty_port' $headersDir/include/linux/tty_flip.h)
if [ $tty_port_param -ne 0 ]; then
   reply="yes"
   USE_TTY_PORT_PARAM="-DnNIKAL100_kUseTtyPortParam"
else
   reply="no"
fi
info "Kernel uses tty_port instead of tty_struct for tty helper functions: $reply"
reply=

DEBUG_VARIANT=""
if [ "$1" = "debug" ]; then
   info " "
   info "NI-KAL will be compiled in debug mode."
   DEBUG_VARIANT="-DnNIKAL100_kDebuggingIsActive"
fi

# JAH 2013/10/30: NOTE: Going forward, please try to use negative logic when
# adding preprocessor defines here.  So instead of setting kHasChainedSGL for newer
# kernels, set kNoChainedSGL for older kernels.  Doing things that way favors
# newer kernels over old outdated ones, which makes long-term maintenance easier,
# and will in most cases eliminate the need for us to add a static PP_DEFINE for
# our Linux RT build (as Linux RT typically uses fairly new kernels).

# Echo all variables to concatenate the strings
KERNEL_VARIANTS="$(echo $REPARENT_VARIANT $MUNMAP_VARIANT $VMMMAP_VARIANT \
$VMMUNMAP_VARIANT $BUSNRES_VARIANT $OFFSET_VARIANT \
$USB_ALTSETTINGS_PTR_VARIANT $USB_INTERFACE_REFCOUNT_VARIANT \
$USB_INTERFACE_CACHE_VARIANT $USB_SET_CONFIGURATION_VARIANT $DEBUG_VARIANT \
$USB_DEVICE_EP_ARRAY_VARIANT $PAGE_TABLE_LEVEL_VARIANT \
$USB_TIMEOUT_UNITS_VARIANT $USB_OWNER_VARIANT \
$MUTEX_VARIANT $CONFIG_VARIANT $IRQ_REGS_VARIANT $WORKQUEUE_VARIANT \
$IOCTL32_VARIANT $COMPLETION_VARIANT $KZALLOC_VARIANT \
$GENERATED_DIR_VARIANT $CHAINED_SGL_VARIANT $IOREMAP_WC_VARIANT \
$X86_CACHEMODE_PROTVAL_VARIANT $CRED_VARIANT $UIDGID_VARIANT \
$VM_RESERVED_VARIANT $UMH_VARIANT $GENLMSG_NEW_VARIANT \
$NAMESPACED_GENETLINK_VARIANT $FAMILY_GENLMSG_PUT_VARIANT \
$FAMILY_GENLOPS_GROUPS_VARIANT $HIGHRES_TIMER_VARIANT $VERSION_VARIANT \
$HAS_CREATE_PROC_READ_ENTRY $HAS_RS485_SUPPORT $HAS_RS485_CONFIG_ON_UART \
$USE_TTY_PORT_PARAM $UART_SET_WAKE_VARIANT $ACPIREMOVE_VARIANT)"

info " "
info "Storing configuration in Makefile.in"
info "If the values stored are incorrect they can be changed before running make."
info " "

dumpTools > ./Makefile.in
echo "KERNELTARGET=$kernelVersion
KERNELDIRECTORY=$headersDir
MODPATH=$niModulePath
LINUX_VERSION_CODE=$LINUX_VERSION_CODE
LINUX_260_VERSION_CODE=$LINUX_260_VERSION_CODE
LINUX_270_VERSION_CODE=$LINUX_270_VERSION_CODE
MIN_SUPPORTED_VERSION_CODE=$LINUX_260_VERSION_CODE
MAX_SUPPORTED_VERSION_CODE=$LINUX_270_VERSION_CODE
KERNEL_VARIANTS=\"$KERNEL_VARIANTS\"" >> ./Makefile.in
