MODULE = phc-intel
VERSION = 0.3.2
# If you are brave enough to use a test release, uncomment the next line.
#VERSION = 0.4.0

# change KERNELSRC to the location of your kernel build tree only if
# autodetection does not work, e.g.:
# KERNELSRC=/usr/src/linux
# or run make with KERNELSRC parameter, e.g.:
# make KERNELSRC=/usr/src/linux
#-------------------------------------------------------------------------------------------------#

# Some targets don't need kernel headers.
ifneq ($(MAKECMDGOALS),)
  ifeq ($(filter-out brave canny clean dkms_check dkms_install dkms_mod_check dkms_mkdeb dkms_mkrpm dkms_uninstall .dkms_version_change, $(MAKECMDGOALS)),)
    SKIPKERNEL := true
  endif
endif

# Where are patches located?
PATCH_BASE := ./inc

ifeq ($(SKIPKERNEL),)
  KERNELSRC ?= /lib/modules/`uname -r`/build
  KERNELRELEASE ?= $(shell grep '[\t ]UTS_RELEASE[\t ][\t ]*"' \
    $(KERNELSRC)/include/linux/version.h \
    $(KERNELSRC)/include/linux/utsrelease.h \
    $(KERNELSRC)/include/generated/utsrelease.h  2>/dev/null | \
    sed -e 's;[^"]*"\(.*\)";\1;g' )

  ifeq ($(KERNELRELEASE),)
    $(error \
    Kernel version not found, maybe you need to install appropriate kernel-headers\
    or run make with KERNELSRC parameter, e.g.: make KERNELSRC=/usr/src/linux)
  endif

  # Strip everything not looking like #.#.# etc. and name it KERNELVERSION.
  SED_KVERSION := sed -n 's/^\([0-9]\{1,\}\(\.[0-9]\{1,\}\)\{1,\}\).*/\1/p'
  ifeq ($(origin KERNELVERSION), undefined)
    KERNELVERSION := $(shell echo $(KERNELRELEASE) | $(SED_KVERSION))
  else
    override KERNELVERSION := $(shell echo $(KERNELVERSION) | $(SED_KVERSION))
  endif

  # Split KERNELVERSION. Naming scheme follows Linux kernel developement with a
  # K_ prefix. EXTRAVERSION can be empty, the rest should not - But kernel 3.0
  # changed the game a bit.
  K_VERSION := $(shell echo $(KERNELVERSION) | cut -d'.' -f1)
  K_PATCHLEVEL := $(shell echo $(KERNELVERSION) | cut -d'.' -f2)
  K_SUBLEVEL := $(shell echo $(KERNELVERSION) | cut -d'.' -f3)
  K_EXTRAVERSION := $(shell echo $(KERNELVERSION) | cut -d'.' -f4)

  ifeq ($(K_VERSION).$(K_PATCHLEVEL), 2.6)
    # Some distributions renamed 3.x kernels to 2.6.y for some time.
    ifeq ($(shell [ "$(K_SUBLEVEL)" -ge 41 -a "$(K_SUBLEVEL)" -le 46 ] && echo true), true)
      SURROGATE := 3.1
    else ifeq ($(K_SUBLEVEL), 40)
      SURROGATE := 3.0
    else
      K_MAJORSUB := $(K_VERSION).$(K_PATCHLEVEL).$(K_SUBLEVEL)
    endif
  else ifeq ($(shell [ "$(K_VERSION)" -ge 3 ] && echo true), true)
    # Latest available patch set in ./inc
    MAX_VERSION = $(shell [ -d "$(PATCH_BASE)" ] && find "$(PATCH_BASE)" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -uV | tail -n1 | cut -d'.' -f1)
    MAX_PATCHSET = $(shell [ -d "$(PATCH_BASE)" ] && find "$(PATCH_BASE)" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -uV | tail -n1 | cut -d'.' -f2)
    ifeq ($(shell [ "$(K_VERSION)" -a "$(MAX_VERSION)" ] && [ "$(K_VERSION)" -lt "$(MAX_VERSION)" ] && echo true), true)
      K_MAJORSUB := $(K_VERSION).$(K_PATCHLEVEL)
    else ifeq ($(K_VERSION), $(MAX_VERSION))
      ifeq ($(shell [ "$(K_PATCHLEVEL)" -a "$(MAX_PATCHSET)" ] && [ "$(K_PATCHLEVEL)" -gt "$(MAX_PATCHSET)" ] && echo true), true)
        SURROGATE := $(K_VERSION).$(MAX_PATCHSET)
      else
        K_MAJORSUB := $(K_VERSION).$(K_PATCHLEVEL)
      endif
    else
      # K_VERSION > MAX_VERSION
      SURROGATE := $(MAX_VERSION).$(MAX_PATCHSET)
    endif
  else
    $(error Your kernel version $(KERNELVERSION) is not supported. Please\
	    drop a message at https://gitlab.com/linux-phc/phc-intel/ if\
	    your kernel version is actually greater or equal to 2.6.27)
  endif

  ifneq ($(SURROGATE),)
    ifeq ($(origin KERNELVERSION), file)
      KERNELVERSION := $(SURROGATE)
    else
      override KERNELVERSION := $(SURROGATE)
    endif
    K_MAJORSUB := $(SURROGATE)
  endif
endif

# Where to install module?
MODULE_DEST := $(DESTDIR)/lib/modules/$(KERNELRELEASE)/extra
MODULE_CONF_D := $(DESTDIR)/etc/modprobe.d
MODULE_CONF := $(MODULE_CONF_D)/$(MODULE).conf
# Which dkms binary to use?
DKMS := $(shell for i in /usr/sbin/dkms $$(command -v dkms); do [ ! -d "$$i" -a -x "$$i" ] && DKMS=$$i; done; echo $$DKMS)
# Where to copy dkms stuff?
DKMS_DEST := $(shell [ -r /etc/dkms/framework.conf ] && . /etc/dkms/framework.conf ; echo $${source_tree:-/usr/src})
DKMS_DEST := $(DKMS_DEST)/$(MODULE)-$(VERSION)
# Stuff for source package creation without root privileges
local_dkms := $(PWD)/.dkms
local_dkmstree := $(local_dkms)
local_dkmssource := $(local_dkms)/$(dir $(DKMS_DEST))

KBUILD_BASENAME=

obj-m += $(MODULE).o

CC=gcc
#EXTRA_CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe
EXTRA_CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -pipe -DCONFIG_ENABLE_MUST_CHECK=y -DCONFIG_ENABLE_WARN_DEPRECATED=y

TARGET := $(MODULE).ko
SOURCE := $(MODULE).c

all: .prepare $(TARGET)

gap := 15
help:
	@printf 'Usage: $(MAKE) [parameters] [target]\n(see $(MAKE) --help for more details)\n\nPossible targets:\n';\
	printf ' %-$(gap).$(gap)s- (default target) build the kernel module\n'			all;\
	printf ' %-$(gap).$(gap)s- install the kernel module to $(MODULE_DEST)/\n'		install;\
	printf ' %-$(gap).$(gap)s- uninstall the kernel module\n'				uninstall;\
	printf ' %-$(gap).$(gap)s- remove all binaries and temporary files\n'			clean;\
	printf ' %-$(gap).$(gap)s- install the module into the DKMS automated build system\n'	dkms_install;\
	printf ' %-$(gap).$(gap)s- uninstall the module from DKMS automated build system\n'	dkms_uninstall;\
	printf ' %-$(gap).$(gap)s- create DKMS deb source package\n'				dkms_mkdeb;\
	printf ' %-$(gap).$(gap)s- create DKMS rpm source package\n'				dkms_mkrpm;\
	printf '\nDKMS-powered targets do include all patches for every supported kernel.\n';\
	printf 'DKMS v2.8.8+ dropped support for building distribution rpm and deb packages.\n';\
	printf '\nPossible parameters:\n';\
	printf ' %-$(gap).$(gap)s- change location of kernel source\n %-$(gap).$(gap)s  default: %s\n' KERNELSRC '' $(KERNELSRC);\
	printf ' %-$(gap).$(gap)s- use specific patch set for building kernel module\n %-$(gap).$(gap)s  default: %s\n' KERNELVERSION '' $(KERNELVERSION);\

$(TARGET): $(SOURCE)
	$(MAKE) -C $(KERNELSRC) M=$(PWD) modules

SET := set -e

# prepare:
# If KERNELVERSION is not specified outside Makefile: use latest available
# patch set, regardless of actuall K_EXTRAVERSION and stuff. Only search for
# exact match if user demands it.
#

.prepare: 
	@$(SET);\
	printf 'Preparing build area for $(MODULE)-$(VERSION):\nsearching for patch directory... '; \
	if [ -d "$(PATCH_BASE)/$(K_MAJORSUB)" ]; then \
		DIR=$$PWD; \
		cd $(PATCH_BASE); \
		if [ "$(origin KERNELVERSION)" = file ]; then \
			PATCHDIR=$$(find -maxdepth 1 \( -name '$(K_MAJORSUB)' -o -name '$(K_MAJORSUB).*' \) -type d -printf '%f\n' | sort -uV | tail -n1) ;\
		else \
			if [ -d "$(KERNELVERSION)" ]; then \
				PATCHDIR=$(KERNELVERSION); \
			else \
				LIST=$$(find -maxdepth 1 \( -name '$(K_MAJORSUB)' -o -name '$(K_MAJORSUB).*' \) -type d -printf '%f\n') ;\
				LIST="$$LIST\n$(KERNELVERSION)" ;\
				PATCHDIR=$$(printf "$$LIST" | sort -uV | sed -n '/^'$(KERNELVERSION)'$$/{g;1!p;};h'); \
			fi; \
		fi; \
		PATCHDIR=$(PATCH_BASE)/$$PATCHDIR; \
		printf 'using %s\n' "$$PATCHDIR"; \
		cd $$DIR; \
		printf 'copying files\n'; \
		cp $$PATCHDIR/acpi-cpufreq.c phc-intel.c; \
		[ ! -e $$PATCHDIR/mperf.h ] || cp $$PATCHDIR/mperf.h .; \
		patch -p1 phc-intel.c $$PATCHDIR/linux-phc-$(VERSION).patch; \
		printf 'SUCCESS. Build area ready.\n\n'; \
	else \
		printf 'FAILED. No suitable patch directory for kernel %s found.\n' "$(KERNELVERSION)"; \
		exit 1; \
	fi

install: $(TARGET)
	install -m 755 -o root -g root -d $(MODULE_CONF_D)
	install -m 644 -o root -g root phc-intel.modprobe $(MODULE_CONF)
	install -m 755 -o root -g root -d $(MODULE_DEST)
	install -m 644 -o root -g root $(TARGET) $(MODULE_DEST)/
	depmod $(KERNELRELEASE) -a

uninstall:
	rm $(MODULE_CONF)
	rm $(MODULE_DEST)/$(TARGET)
	depmod $(KERNELRELEASE) -a

clean:
	rm -f *~ *.o *.s *.ko *.mod.c .*.cmd *.rej *.c.orig Module.symvers Module.markers modules.order $(SOURCE) mperf.h *.deb *.rpm *.old
	rm -rf .tmp_versions $(local_dkms)

.dkms_check:
	@$(SET);\
	[ "$(DKMS)" ] || (\
		printf 'ERROR: Unable to locate dkms.\n';\
		exit 1;\
	)

.dkms_mod_check:
	@$(SET);\
	[ -z "$(shell $(DKMS) status -m $(MODULE))" ] || (\
		printf 'ERROR: Previous DKMS installations found. Use `$(MAKE) dkms_uninstall` to remove all of them.\n';\
		exit 1;\
	)

dkms_install: .dkms_check .dkms_mod_check
	@$(SET);\
	for DIR in $(wildcard $(PATCH_BASE)/*); do\
		install -m 755 -o root -g root -d $(DKMS_DEST)/$$DIR;\
		install -m 644 -o root -g root $$DIR/* $(DKMS_DEST)/$$DIR;\
	done;\
	install -m 644 -o root -g root dkms.conf Makefile $(DKMS_DEST);\
	$(DKMS) add $(DKMS_DEST)
	$(DKMS) install -m $(MODULE) -v $(VERSION)

dkms_uninstall: .dkms_check
	@$(SET);\
	CANDIDATE="$$($(DKMS) status -m $(MODULE))";\
	printf 'This will remove *all* dkms installations of $(MODULE) from your system:\n%s\n\nPlease type YES to confirm: ' "$$CANDIDATE";\
	read input; [ "$$input" = YES ] || exit 0;\
	printf '%s\n' "$$CANDIDATE" | while read head version tail; do\
		head_tmp=$${head#*/};\
		if [ "$$head_tmp" = "$$head" ]; then\
			:;\
		else\
			version=$$head_tmp;\
		fi;\
		[ $$version ] || break;\
		$(DKMS) remove -m $(MODULE) -v $${version%[,:]} --all;\
	done;\
	rm -rf $(dir $(DKMS_DEST))$(MODULE)-*

dkms_mkdeb dkms_mkrpm: .dkms_check
	@$(SET);\
	KIND=$(subst dkms_,,$@);\
	rm -rf $(local_dkms);\
	for DIR in $(wildcard $(PATCH_BASE)/*); do\
		install -m 755 -d $(local_dkms)/$(DKMS_DEST)/$$DIR;\
		install -m 644 $$DIR/* $(local_dkms)/$(DKMS_DEST)/$$DIR;\
	done;\
	install -m 644 dkms.conf Makefile $(local_dkms)/$(DKMS_DEST);\
	$(DKMS) add $$KIND --source-only -m $(MODULE) -v $(VERSION) --dkmstree $(local_dkmstree) --sourcetree $(local_dkmssource);\
	mv $(local_dkmstree)/$(MODULE)/$(VERSION)/$${KIND#mk}/* .;\
	printf '\nSource package ready to install and available at\n';\
	printf ' $(PWD)/%s\n' *.$${KIND#mk} ;

brave canny:
	@$(SET);\
	[ $@ = canny ] && SUB=#;\
	sed -i.old '/^[ \t]*# If you are brave/{n;s/^[ \t]*#*/'$$SUB'/;}' Makefile;\
	$(MAKE) -s .dkms_version_change

.dkms_version_change:
	@$(SET);\
	sed -i.old 's/^\(PACKAGE_VERSION=\).*/\1"$(VERSION)"/' dkms.conf;

.PHONY: help .prepare install uninstall clean .dkms_check .dkms_mod_check dkms_install dkms_uninstall dkms_mkdeb dkms_mkrpm brave canny .dkms_version_change
.NOTPARALLEL:
