Przeglądaj źródła

Treiber erweitert. Stub für gfa_spi.

Rind 4 lat temu
rodzic
commit
796dc7481e
22 zmienionych plików z 2313 dodań i 361 usunięć
  1. 21 0
      .gitignore
  2. 12 0
      GfaSpiStub/.gitignore
  3. 5 13
      GfaSpiStub/gfaspistub.pro
  4. 560 0
      GfaSpiStub/src/main.cpp
  5. 4 3
      Makefile
  6. BIN
      bin/gfaspi.ko
  7. BIN
      bin/gfaspistub
  8. 53 5
      defines.h
  9. 158 29
      drvmain.c
  10. 23 0
      kfile.c
  11. 1 0
      kfile.h
  12. 527 0
      krtc.c
  13. 33 0
      krtc.h
  14. 28 9
      kspi.c
  15. 18 3
      ksync.c
  16. 2 0
      ksync.h
  17. 171 5
      ktiva.c
  18. 23 1
      ktiva.h
  19. 517 37
      sfsattrib.c
  20. 29 8
      sfsattrib.h
  21. 0 248
      test/main.c
  22. 128 0
      timeconv.c

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+
+btm43_0113/
+gfa_spi/
+legacy/
+test/
+.tmp_versions/
+*.bak
+*.dwo
+*.cmd
+*.o
+*.ko
+!bin/*.ko
+*.order
+*.mod.c
+*.symvers

+ 12 - 0
GfaSpiStub/.gitignore

@@ -0,0 +1,12 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+
+Debug/
+Profile/
+Release/
+*.pro.user
+*.bak

+ 5 - 13
test/spitest.pro → GfaSpiStub/gfaspistub.pro

@@ -1,31 +1,23 @@
 TEMPLATE = app
-CONFIG += console c++11 thread
-CONFIG -= qt app_bundle
+CONFIG += console
+CONFIG -= app_bundle qt
 
 QMAKE_LIBDIR += $$[QT_SYSROOT]/usr/lib/gfa
 QMAKE_RPATHDIR += /usr/lib/gfa
-#QMAKE_LIBS += -ludev
 
 CONFIG(debug, debug|release) {
     QMAKE_CXXFLAGS -= -Os
     QMAKE_CFLAGS -= -Os
     QMAKE_CXXFLAGS += -D_DEBUG
     QMAKE_CFLAGS += -D_DEBUG
-	QMAKE_LIBS += -pthread
-}
-
-CONFIG(release, debug|release) {
-	QMAKE_LIBS += -pthread
 }
 
 linux-buildroot-g++ {
     QMAKE_CXXFLAGS += -D_TARGET_BUILD
     QMAKE_CFLAGS += -D_TARGET_BUILD
-    target.path += /opt/GfA/spi
-	INSTALLS += target
+	target.path  = /root
+	INSTALLS    += target
 }
 
 SOURCES += \
-    main.c
-
-HEADERS +=
+    src/main.cpp

+ 560 - 0
GfaSpiStub/src/main.cpp

@@ -0,0 +1,560 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <limits.h>
+#include <getopt.h>
+#include "../gfaspi.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define TRACE(...)							fprintf(stdout, __VA_ARGS__), fflush(stdout)
+#define ETRACE(...)							fprintf(stderr, __VA_ARGS__), fflush(stderr)
+#define _countof(a)							(sizeof(a) / sizeof(*a))
+#define UNUSED(arg)							(void)arg
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _GFA_SYSFS_PATH_ROOT				"/sys/gfa/tiva/"
+#define _GFA_SYSFS_PATH_RTC_TIMESTAMP		_GFA_SYSFS_PATH_ROOT "rtc/uxts64bin"
+#define _GFA_SYSFS_PATH_RTC_CTRL			_GFA_SYSFS_PATH_ROOT "rtc/ctrl"
+#define _GFA_SYSFS_PATH_RTC_TYPE			_GFA_SYSFS_PATH_ROOT "rtc/type"
+#define _GFA_SYSFS_PATH_MEM_RW				_GFA_SYSFS_PATH_ROOT "mem/mem"
+#define _GFA_SYSFS_PATH_FIRMWARE_VERSION	_GFA_SYSFS_PATH_ROOT "firmware/version"
+#define _GFA_SYSFS_PATH_ADC_BIN				_GFA_SYSFS_PATH_ROOT "adc/AdcBin"
+#define _GFA_SYSFS_PATH_UPTIME				_GFA_SYSFS_PATH_ROOT "uptime"
+#define _GFA_SYSFS_PATH_BACKLIGHT_DUTYCYCLE	_GFA_SYSFS_PATH_ROOT "backlight/dutycycle"
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef enum
+{
+	Cmd_Inval,
+	Cmd_Sys2Rtc,
+	Cmd_Rtc2Sys,
+	Cmd_GetRtc,
+	Cmd_TestI2C,
+	Cmd_ReadAddr,
+	Cmd_WriteAddr,
+	Cmd_GetFwVer,
+	Cmd_GetADC,
+	Cmd_GetUptime,
+	Cmd_SetBacklight
+}Commands;
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static int _FileGetContent(const char *pszFilepath, void *pBuffer, size_t nCbBuffer)
+{
+	int fd, ret = 0;
+	
+	if((fd = open(pszFilepath, O_RDONLY)) >= 0)
+	{
+		ret = read(fd, pBuffer, nCbBuffer);
+		close(fd);
+	}
+	else
+		ret = fd;
+
+	return ret;
+}
+
+static int _FileSetContent(const char *pszFilepath, const void *pBuffer, size_t nCbBuffer)
+{
+	int fd, ret = 0;
+	
+	if((fd = open(pszFilepath, O_WRONLY)) >= 0)
+	{
+		ret = write(fd, pBuffer, nCbBuffer);
+		close(fd);
+	}
+	else
+		ret = fd;
+
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static unsigned int _LegacyGetTivaTemp(void)
+{
+	TIVA_ADC tadc;
+	int ret = _FileGetContent(_GFA_SYSFS_PATH_ADC_BIN, &tadc, sizeof(tadc));
+	if(ret == sizeof(tadc))
+		return (unsigned int)tadc.TempTIVA;
+	return 0xFFFFFFFF;
+}
+
+static int _LegacySetBacklightBrightness(unsigned int val)
+{
+	int nLen, nRet = -1;
+	char szBuf[256];
+	if((nLen = sprintf(szBuf, "%u", val)) > 0)
+	{
+		if((nRet = _FileSetContent(_GFA_SYSFS_PATH_BACKLIGHT_DUTYCYCLE, szBuf, nLen)) == nLen)
+			nRet = 0;
+	}
+	return nRet;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef unsigned int (*PFN_READ_ADDR_HANDLER)(void);
+typedef int (*PFN_WRITE_ADDR_HANDLER)(unsigned int);
+
+typedef struct _TIVA_LEGACY_ADDRESS
+{
+	unsigned int addr;	// Tiva address
+	PFN_READ_ADDR_HANDLER pfnRead;
+	PFN_WRITE_ADDR_HANDLER pfnWrite;
+}TIVA_LEGACY_ADDRESS, *LPTIVA_LEGACY_ADDRESS;
+typedef const TIVA_LEGACY_ADDRESS *LPCTIVA_LEGACY_ADDRESS;
+
+static const TIVA_LEGACY_ADDRESS g_tLegAddrMap[] =
+{
+	{
+		0x200003A0,
+		_LegacyGetTivaTemp,
+		NULL
+	},
+	{
+		0x200004E4,
+		NULL,
+		_LegacySetBacklightBrightness
+	}
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+static LPCTIVA_LEGACY_ADDRESS _LookupLegacyAddressMap(unsigned int addr)
+{
+	for(size_t i = 0; i < _countof(g_tLegAddrMap); ++i)
+	{
+		LPCTIVA_LEGACY_ADDRESS pla = &g_tLegAddrMap[i];
+		if(pla->addr == addr)
+			return pla;
+	}
+	return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static bool _StrToULong(const char *pszNum, unsigned long &l)
+{
+	char *pszEndPtr = NULL;
+	unsigned long v = strtoul(pszNum, &pszEndPtr, 16);
+	
+	if(((v == ULONG_MAX) && (errno == ERANGE)) || *pszEndPtr)
+		return false;
+	l = v;
+	return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static void _ShowUsage(void)
+{
+    TRACE("\ngfaspistub V 1.0\n"
+			"  Stub to replace gfa_spi.\n  Implements gfa_spi command interface using GfA SysFs.\n"
+			"  Usage: gfaspistub [opt]\n"
+			"    -w System Time to RTC\n"
+			"    -s RTC to System Time\n"
+			"    -r Read RTC\n"
+			"    -t Test I2C\n"
+			"    -i <Addr HEX>, Read from addr\n"
+			"    -o <Addr HEX> <data HEX>, Write to addr\n"
+			"    -v Get Firmware Version\n"
+			"    -a Get Analog Values\n"
+			"    -u Tiva Uptime\n"
+			"    -b Backlight in %% (0 .. 100)\n");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static int _GetRtcType(void)
+{
+	char szBuf[256];
+	int ret = _FileGetContent(_GFA_SYSFS_PATH_RTC_TYPE, szBuf, sizeof(szBuf));
+	
+	if(ret > 0)
+	{
+		szBuf[ret] = '\0';
+		if(!strncmp(szBuf, "MCP7940", 7))
+			return 0;
+		else if(!strncmp(szBuf, "DS3231", 6))
+			return 1;
+	}
+
+	return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[])
+{
+	int fd, nRet = 0, nLen, cmd, access = O_RDONLY, rtcType = -1;
+	unsigned int cmdCnt = 0;
+	unsigned long opt1 = 0xFFFFFFFF, opt2 = 0xFFFFFFFF;
+	bool bHasOpt1 = false, bHasOpt2 = false;
+	const char *pszSysFsPath = NULL;
+	unsigned char ctrl = 0;
+	char szBuf[256];
+	long long ts;
+	TIVA_ADC tadc;
+	Commands command = Cmd_Inval;
+	LPCTIVA_LEGACY_ADDRESS ptla;
+
+	/////////////////////////////////////////////////////////////////////////
+	// parse command line options
+
+	if(argc < 2)
+	{
+		ETRACE("Error: No command provided!\n");
+		_ShowUsage();
+		return -1;
+	}
+
+	while((cmd = getopt(argc, argv, "-:wsrti:o:vaub:")) != -1)
+	{
+		switch(cmd)
+		{
+		case 1:
+			if(!_StrToULong(optarg, opt2))
+			{
+				ETRACE("Invalid argument: %s\n", optarg);
+				return -1;
+			}
+			bHasOpt2 = true;
+			break;
+		case 'w':		// SystemTime to RTC
+			command = Cmd_Sys2Rtc;
+			pszSysFsPath = _GFA_SYSFS_PATH_RTC_CTRL;
+			access = O_RDWR;
+			ctrl = 1;
+			++cmdCnt;
+			break;
+		case 's':		// RTC to SystemTime
+			command = Cmd_Rtc2Sys;
+			pszSysFsPath = _GFA_SYSFS_PATH_RTC_CTRL;
+			access = O_RDWR;
+			ctrl = 2;
+			++cmdCnt;
+			break;
+		case 'r':		// Read RTC
+			command = Cmd_GetRtc;
+			pszSysFsPath = _GFA_SYSFS_PATH_RTC_TIMESTAMP;
+			++cmdCnt;
+			break;
+		case 't':		// testI2C
+			command = Cmd_TestI2C;
+			pszSysFsPath = _GFA_SYSFS_PATH_RTC_CTRL;
+			access = O_RDWR;
+			ctrl = 3;
+			++cmdCnt;
+			break;
+		case 'i':		// <Addr HEX> read from addr
+			if(!_StrToULong(optarg, opt1))
+			{
+				ETRACE("Invalid argument: %s\n", optarg);
+				_ShowUsage();
+				return -1;
+			}
+			command = Cmd_ReadAddr;
+			bHasOpt1 = true;
+			pszSysFsPath = _GFA_SYSFS_PATH_MEM_RW;
+			++cmdCnt;
+			break;
+		case 'o':		// <Addr HEX> <data HEX> write to addr
+			if(!_StrToULong(optarg, opt1))
+			{
+				ETRACE("Invalid argument: %s\n", optarg);
+				_ShowUsage();
+				return -1;
+			}
+			command = Cmd_WriteAddr;
+			bHasOpt1 = true;
+			pszSysFsPath = _GFA_SYSFS_PATH_MEM_RW;
+			access = O_RDWR;
+			++cmdCnt;
+			break;
+		case 'v':		// Get firmware version
+			command = Cmd_GetFwVer;
+			pszSysFsPath = _GFA_SYSFS_PATH_FIRMWARE_VERSION;
+			++cmdCnt;
+			break;
+		case 'a':		// Get Analog Values
+			command = Cmd_GetADC;
+			pszSysFsPath = _GFA_SYSFS_PATH_ADC_BIN;
+			++cmdCnt;
+			break;
+		case 'u':		// Tiva uptime
+			command = Cmd_GetUptime;
+			pszSysFsPath = _GFA_SYSFS_PATH_UPTIME;
+			++cmdCnt;
+			break;
+		case 'b':		// Backlight in %, <0-100>
+			if(!_StrToULong(optarg, opt1))
+			{
+				ETRACE("Invalid argument: %s\n", optarg);
+				_ShowUsage();
+				return -1;
+			}
+			command = Cmd_SetBacklight;
+			bHasOpt1 = true;
+			pszSysFsPath = _GFA_SYSFS_PATH_BACKLIGHT_DUTYCYCLE;
+			access = O_WRONLY;
+			++cmdCnt;
+			break;
+		case ':':
+			break;
+		case '?':
+			_ShowUsage();
+			return 1;
+		default:
+			_ShowUsage();
+			return 1;
+		}
+
+		if(cmdCnt > 1)
+		{
+			ETRACE("Error: Only one command may be provided!\n");
+			_ShowUsage();
+			return -1;
+		}
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// validate command line options
+	
+	switch(command)
+	{
+	case Cmd_Inval:
+		ETRACE("Error: Invalid command or missing arguments!\n");
+		_ShowUsage();
+		return -1;
+	case Cmd_ReadAddr:
+		if(bHasOpt2)
+		{
+			ETRACE("Error: Invalid argument(s)!\n");
+			_ShowUsage();
+			return -1;
+		}
+		if(bHasOpt1)
+			break;
+	case Cmd_WriteAddr:
+		if(!bHasOpt1)
+		{
+			ETRACE("Error: No address provided!\n");
+			_ShowUsage();
+			return -1;
+		}
+		if(!bHasOpt2)
+		{
+			ETRACE("Error: Missing value!\n");
+			_ShowUsage();
+			return -1;
+		}
+		break;
+	case Cmd_SetBacklight:
+		if(!bHasOpt1)
+		{
+			ETRACE("Error: No value provided!\n");
+			_ShowUsage();
+			return -1;
+		}
+		break;
+	case Cmd_Sys2Rtc:
+	case Cmd_Rtc2Sys:
+	case Cmd_GetRtc:
+		if((rtcType = _GetRtcType()) < 0)
+		{
+			ETRACE("Error: RTC-Type unknown!\n");
+			return -1;
+		}
+		break;
+	default:
+		if(bHasOpt1 || bHasOpt2)
+		{
+			ETRACE("Error: Invalid argument(s)!\n");
+			_ShowUsage();
+			return -1;
+		}
+		break;
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// execute command
+	
+	if((fd = open(pszSysFsPath, access)) < 0)
+	{
+		ETRACE("Error opening SysFs: %s!\n", strerror(errno));
+		return -1;
+	}
+
+	switch(command)
+	{
+	case Cmd_Sys2Rtc:
+		if((nRet = write(fd, &ctrl, 1)) == 1)
+		{
+			char szTime[256];
+			time_t t = time(NULL);
+			struct tm *ptm = gmtime(&t);
+			strftime(szTime, sizeof(szTime), "%y/%m/%d,%H:%M:%S", ptm);
+			TRACE("SYSTIME(%d) \"%s\"\n", rtcType, szTime);
+			nRet = 0;
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_Rtc2Sys:
+		if((nRet = write(fd, &ctrl, 1)) == 1)
+		{
+			char szTime[256];
+			time_t t = time(NULL);
+			struct tm *ptm = gmtime(&t);
+			strftime(szTime, sizeof(szTime), "%y/%m/%d,%H:%M:%S", ptm);
+			TRACE("RTCTIME(ok/%d) \"%s\"\n", rtcType, szTime);
+			nRet = 0;
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_GetRtc:
+		if((nRet = read(fd, &ts, sizeof(ts))) == sizeof(ts))
+		{
+			char szTime[256];
+			time_t t = (time_t)ts;
+			struct tm *ptm = gmtime(&t);
+			strftime(szTime, sizeof(szTime), "%y/%m/%d,%H:%M:%S", ptm);
+			TRACE("RTCTIME(ok/%d) \"%s\"\n", rtcType, szTime);
+			nRet = 0;
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_TestI2C:
+		if((nRet = write(fd, &ctrl, 1)) == 1)
+		{
+			TRACE("I2C-Test Success!\n");
+			nRet = 0;
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_ReadAddr:
+		opt1 &= 0xFFFFFFFCUL;
+		if((ptla = _LookupLegacyAddressMap((unsigned int)opt1)) && ptla->pfnRead)
+		{
+			unsigned int val = (*ptla->pfnRead)();
+			TRACE("Addr:%08lX Data:%08X\n", opt1, val);
+			nRet = 0;
+		}
+		else if((nRet = lseek(fd, (off_t)opt1, SEEK_SET)) == (off_t)opt1)
+		{
+			unsigned int val;
+			if((nRet = read(fd, &val, sizeof(val))) == sizeof(val))
+			{
+				TRACE("Addr:%08lX Data:%08X\n", opt1, val);
+				nRet = 0;
+			}
+			else
+				ETRACE("Error: %s!\n", strerror(errno));
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_WriteAddr:
+		opt1 &= 0xFFFFFFFCUL;
+		if((ptla = _LookupLegacyAddressMap((unsigned int)opt1)) && ptla->pfnWrite)
+		{
+			if(!(nRet = (*ptla->pfnWrite)((unsigned int)opt2)))
+				TRACE("Addr:%08lX ret:%d\n", opt1, nRet);
+			else
+				ETRACE("Error: %s!\n", strerror(errno));
+		}
+		else if((nRet = lseek(fd, (off_t)opt1, SEEK_SET)) == (off_t)opt1)
+		{
+			unsigned int val = (unsigned int)opt2;
+			if((nRet = write(fd, &val, sizeof(val))) == sizeof(val))
+			{
+				TRACE("Addr:%08lX ret:0\n", opt1);
+				nRet = 0;
+			}
+			else
+				ETRACE("Error: %s!\n", strerror(errno));
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_GetFwVer:
+		if((nRet = read(fd, szBuf, sizeof(szBuf))) > 0)
+		{
+			if(sscanf(szBuf, "%lu %lu", &opt1, &opt2) == 2)
+			{
+			    TRACE("Tivia Firmware Version HW : 0x%.8lX SW: 0x%.8lX\n", opt1, opt2);
+				nRet = 0;
+			}
+			else
+				ETRACE("Error: %s!\n", strerror(errno));
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_GetADC:
+		if((nRet = read(fd, &tadc, sizeof(tadc))) == sizeof(tadc))
+		{
+			TRACE("UVers    : %5.2f\n", (double)tadc.UVers / 100.0 + 0.4);
+			TRACE("UBATV3   : %5.2f\n", (double)tadc.UBatV3 / 100.0);
+			TRACE("TEMP     : %5.2f\n", (double)tadc.Temp / 10.0);
+			TRACE("UV5VSYS  : %5.2f\n", (double)tadc.UV5Vsys / 100.0);
+			TRACE("UV3V6BAT : %5.2f\n", (double)tadc.UV3V6Bat / 100.0);
+			TRACE("TEMPTIVA : %5.2f\n", 147.5 - 187.5 * (double)tadc.TempTIVA / 4096.0);
+			nRet = 0;
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_GetUptime:
+		if((nRet = read(fd, szBuf, sizeof(szBuf))) > 0)
+		{
+			if(sscanf(szBuf, "%lu", &opt1) == 1)
+			{
+				opt1		/= 60000;
+				int day		= opt1 / 1440;
+				int hour	= (opt1 % 1440) / 60;
+				int min		= (opt1 % 1440) % 60;
+			    TRACE("%d d %02d:%02d\n", day, hour, min);
+				nRet = 0;
+			}
+			else
+				ETRACE("Error: %s!\n", strerror(errno));
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	case Cmd_SetBacklight:
+		if((nLen = sprintf(szBuf, "%lu", opt1)) > 0)
+		{
+			if((nRet = write(fd, szBuf, nLen)) != nLen)
+				ETRACE("Error: %s!\n", strerror(errno));
+			else
+				nRet = 0;
+		}
+		else
+			ETRACE("Error: %s!\n", strerror(errno));
+		break;
+	default:
+		break;
+	}
+
+	close(fd);
+	return nRet;
+}

+ 4 - 3
Makefile

@@ -2,7 +2,7 @@
 TARGET_MODULE:=gfaspi
 PWD := $(shell pwd)
 
-$(TARGET_MODULE)-objs := drvmain.o kfile.o sfsattrib.o kspi.o ktiva.o kfirmware.o ksync.o
+$(TARGET_MODULE)-objs := drvmain.o kfile.o sfsattrib.o kspi.o ktiva.o kfirmware.o ksync.o krtc.o timeconv.o
 obj-m := $(TARGET_MODULE).o
 
 
@@ -10,15 +10,16 @@ obj-m := $(TARGET_MODULE).o
 
 default:
 	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
+	@mkdir -p bin
+	@cp -f $(TARGET_MODULE).ko -tbin
 
 clean:
 	$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
-
+	@rm -f bin/$(TARGET_MODULE).ko
 
 depend .depend dep:
 	$(CC) -M *.c > .depend
 
-
 ifeq (.depend,$(wildcard .depend))
 include .depend
 endif

BIN
bin/gfaspi.ko


BIN
bin/gfaspistub


+ 53 - 5
defines.h

@@ -16,6 +16,7 @@ extern "C" {
 #define _SITARA_EGGELSBERG										0
 #define _EXTENDED_ERROR_CHECK									1
 #define _SUPPORT_LEGACY_UPTIME									0
+#define _SCALE_DISPLAY_DUTY_CYCLE								0
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -29,6 +30,51 @@ extern "C" {
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
 
+typedef union _TIVA_DCAP
+{
+	struct
+	{
+		unsigned int did0;
+		unsigned int did1;
+		unsigned int dc0;
+	};
+	struct
+	{
+		unsigned int min	: 8;	// Minor Die Revision
+		unsigned int maj	: 8;	// Major Die Revision
+		unsigned int cls	: 8;	// Device Class
+		unsigned int res2	: 4;
+		unsigned int ver0	: 3;	// DID0 Version
+		unsigned int res1	: 1;
+
+		unsigned int qual	: 2;	// Qualification Status
+		unsigned int rohs	: 1;	// RoHS-Compliance
+		unsigned int pkg	: 2;	// Package Type
+		unsigned int temp	: 3;	// Temperature Range
+		unsigned int res3	: 5;
+		unsigned int pincnt	: 3;	// Package Pin Count
+		unsigned int partno	: 8;	// Part Number
+		unsigned int fam	: 4;	// Family
+		unsigned int ver1	: 4;	// DID1 Version
+		
+		unsigned short fsize;		// Flash Size
+		unsigned short ssize;		// SRAM Size
+	};
+}TIVA_DCAP, *LPTIVA_DCAP;
+typedef const TIVA_DCAP *LPCTIVA_DCAP;
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _TIVA_MEMORY_MAP
+{
+	unsigned int baseAddr;
+	unsigned int size;
+	bool bIsRO;
+}TIVA_MEMORY_MAP, *LPTIVA_MEMORY_MAP;
+typedef const TIVA_MEMORY_MAP *LPCTIVA_MEMORY_MAP;
+
+/////////////////////////////////////////////////////////////////////////////
+
 typedef struct _TIVA_MAT_SER
 {
 	char szImgMaterialNum[KFW_MAX_IMG_MATERIAL_NUM_LENGTH];
@@ -83,7 +129,6 @@ typedef const TIVA_UPLOAD_INFO *LPCTIVA_UPLOAD_INFO;
 #define _JIFFY_DIFF(a, b)				((long)((unsigned long)(a) - (unsigned long)(b)))
 #define KALERT(...)						printk(KERN_ALERT __VA_ARGS__)
 
-
 /////////////////////////////////////////////////////////////////////////////
 
 
@@ -98,10 +143,13 @@ typedef const TIVA_UPLOAD_INFO *LPCTIVA_UPLOAD_INFO;
 #define _FIRMWARE_PAGES_COUNT			6
 #define _FIRMWARE_BUFFER_SIZE			(64 * PAGE_SIZE) // = 2 ^ 6 * PAGE_SIZE
 
-#define _BACKLIGHT_FREQ_MIN_HZ			10
-#define _BACKLIGHT_FREQ_MAX_HZ			10000
-#define _BACKLIGHT_DEF_FREQ_HZ			1000
-#define _BACKLIGHT_DEF_BRIGHT_PERC		50
+#define _BACKLIGHT_PERIOD_MIN			5000
+#define _BACKLIGHT_PERIOD_MAX			1000000
+#define _BACKLIGHT_PERIOD_DEFAULT		50000
+
+#define _BACKLIGHT_DUTY_CYCLE_PERC_MIN	0
+#define _BACKLIGHT_DUTY_CYCLE_PERC_MAX	100
+#define _BACKLIGHT_DUTY_CYCLE_PERC_DEF	100
 
 #define KFW_DEFAULT_PAGE_ERASE_TIME		20
 #define KFW_FLASH_PAGE_SIZE				1024

+ 158 - 29
drvmain.c

@@ -29,8 +29,8 @@ MODULE_AUTHOR("GfA");
 /////////////////////////////////////////////////////////////////////////////
 // sys fs
 
-static struct kobject *g_pKoGfa = NULL, *g_pKoTiva = NULL, *g_pKoBacklight = NULL, *g_pKoADC = NULL, *g_pKoFirmware = NULL;
-static bool g_bHasFwVersion = false;
+static struct kobject *g_pKoGfa = NULL, *g_pKoTiva = NULL, *g_pKoBacklight = NULL, *g_pKoADC = NULL, *g_pKoFirmware = NULL, *g_pKoMem = NULL, *g_pKoRtc = NULL;
+static bool g_bHasFwVersion = false, g_bHasDeviceCaps = false;
 static atomic_t g_flgSysFsRunning;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -158,12 +158,12 @@ static void _BacklightWorkProc(struct work_struct *work)
 	struct file *pfSpiDev = NULL;
 	int ret;
 
-	if((pfSpiDev = kf_open(_SPI_DEVICE, O_RDWR, 0)))
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
 	{
 		do
 		{
-			unsigned int nFrequency = SfAttGetBacklightFrequency();
-			unsigned int nBrightness = SfAttGetBacklightBrightness();
+			unsigned int nPeriod = SfAttGetBacklightPeriod();
+			unsigned int nDutyCyclePerc = SfAttGetBacklightDutyCyclePercent();
 
 			/////////////////////////////////////////////////////////////////
 
@@ -174,8 +174,8 @@ static void _BacklightWorkProc(struct work_struct *work)
 
 			/////////////////////////////////////////////////////////////////
 
-			KALERT("%s: Set backlight frequency to %u Hz, brightness to %u %%\n", __FUNCTION__, nFrequency, nBrightness);
-			if((ret = TivaCmdSetBacklight(pfSpiDev, nFrequency, nBrightness)) != 0)
+			KALERT("%s: Set backlight period to %u, duty cycle to %u %%\n", __FUNCTION__, nPeriod, nDutyCyclePerc);
+			if((ret = TivaCmdSetBacklight(pfSpiDev, nPeriod, nDutyCyclePerc)) != 0)
 			{
 				KALERT("%s: TivaCmdSetBacklight failed: %d!\n", __FUNCTION__, ret);
 				break;
@@ -187,7 +187,7 @@ static void _BacklightWorkProc(struct work_struct *work)
 	}
 	else
 	{
-		KALERT("%s: kf_open failed\n", __FUNCTION__);
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
 	}
 
 	if(_GetTimerRunning())
@@ -214,7 +214,7 @@ static void _FwUploadWorkProc(struct work_struct *work)
 		return;
 	}
 
-	if((pfSpiDev = kf_open(_SPI_DEVICE, O_RDWR, 0)))
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
 	{
 		do
 		{
@@ -379,7 +379,7 @@ static void _FwUploadWorkProc(struct work_struct *work)
 	}
 	else
 	{
-		KALERT("%s: kf_open failed\n", __FUNCTION__);
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
 	}
 
 	if(!bSuccess)
@@ -409,7 +409,7 @@ static void _SysFsWorkProc(struct work_struct *work)
 	unsigned long long nUpTime;
 	struct file *pfSpiDev = NULL;
 
-	if((pfSpiDev = kf_open(_SPI_DEVICE, O_RDWR, 0)))
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
 	{
 		do
 		{
@@ -431,22 +431,48 @@ static void _SysFsWorkProc(struct work_struct *work)
 			ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES);
 
 			/////////////////////////////////////////////////////////////////
-			// firmware
+			// device capabilities
 
-			if(!g_bHasFwVersion)
+			if(!g_bHasDeviceCaps)
 			{
-				if((ret = TivaCmdGetFirmwareVersion(pfSpiDev, &hw, &sw)) == 0)
+				TIVA_DCAP dcap;
+
+				if((ret = TivaCmdGetDeviceCaps(pfSpiDev, &dcap)) == 0)
+				{
+					ksync_lock();
+					g_dcaps = dcap;
+					g_tiMM[0].size = KTIVA_FLASH_SIZE(dcap.fsize);	// Flash
+					g_tiMM[1].size = KTIVA_SRAM_SIZE(dcap.ssize);	// SRAM
+					g_tiMM[2].size = g_tiMM[1].size * 32;			// Bit banded SRAM
+					ksync_unlock();
+					g_bHasDeviceCaps		= true;
+				}
+				else
 				{
-					if((g_hw != hw) || (g_sw != sw))
+					if(_IS_REVIVE_STATE(ret))
 					{
-						ksync_lock();
-						g_hw = hw;
-						g_sw = sw;
-						ksync_unlock();
-						sysfs_notify(g_pKoFirmware, NULL, "version");
-						KALERT("%s: TivaCmdGetFirmwareVersion: HW %d, SW: %d\n", __FUNCTION__, g_hw, g_sw);
+						KALERT("%s: TivaCmdGetFirmwareVersion failed: %d - call TivaRevive\n", __FUNCTION__, ret);
+						TivaRevive(pfSpiDev);
 					}
+					break;
+				}
+
+				ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES);
+			}
+
+			/////////////////////////////////////////////////////////////////
+			// firmware
 
+			if(!g_bHasFwVersion)
+			{
+				if((ret = TivaCmdGetFirmwareVersion(pfSpiDev, &hw, &sw)) == 0)
+				{
+					ksync_lock();
+					g_hw = hw;
+					g_sw = sw;
+					ksync_unlock();
+					sysfs_notify(g_pKoFirmware, NULL, "version");
+//					KALERT("%s: TivaCmdGetFirmwareVersion: HW %d, SW: %d\n", __FUNCTION__, g_hw, g_sw);
 					g_bHasFwVersion = true;
 				}
 				else
@@ -581,7 +607,7 @@ static void _SysFsWorkProc(struct work_struct *work)
 	}
 	else
 	{
-		KALERT("%s: kf_open failed\n", __FUNCTION__);
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
 	}
 
 	if(_GetTimerRunning())
@@ -630,9 +656,27 @@ static int drv_init(void)
 			break;
 		}
 
+		if(!(g_pKoMem = kobject_create_and_add("mem", g_pKoTiva)))
+		{
+			KALERT("kobject_create_and_add failed\n");
+			break;
+		}
+
+		if(!(g_pKoRtc = kobject_create_and_add("rtc", g_pKoTiva)))
+		{
+			KALERT("kobject_create_and_add failed\n");
+			break;
+		}
+
 		/////////////////////////////////////////////////////////////////////
 		// generic
 
+		if(sysfs_create_file(g_pKoTiva, &g_tivaDevIdAtt.attr))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
 		if(sysfs_create_file(g_pKoTiva, &g_tivaUptimeAtt.attr))
 		{
 			KALERT("sysfs_create_file failed\n");
@@ -660,13 +704,13 @@ static int drv_init(void)
 		/////////////////////////////////////////////////////////////////////
 		// backlight
 
-		if(sysfs_create_file(g_pKoBacklight, &g_tivaBrightnessAtt.attr))
+		if(sysfs_create_file(g_pKoBacklight, &g_tivaDutyCycleAtt.attr))
 		{
 			KALERT("sysfs_create_file failed\n");
 			break;
 		}
 
-		if(sysfs_create_file(g_pKoBacklight, &g_tivaFrequencyAtt.attr))
+		if(sysfs_create_file(g_pKoBacklight, &g_tivaPeriodAtt.attr))
 		{
 			KALERT("sysfs_create_file failed\n");
 			break;
@@ -717,6 +761,48 @@ static int drv_init(void)
 			break;
 		}
 
+		/////////////////////////////////////////////////////////////////////
+		// Memory
+
+		if(sysfs_create_bin_file(g_pKoMem, &g_tivaMemory))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
+		if(sysfs_create_file(g_pKoMem, &g_tivaMemMap.attr))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
+		/////////////////////////////////////////////////////////////////////
+		// RTC
+
+		if(sysfs_create_file(g_pKoRtc, &g_tivaRtcIso8601.attr))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
+		if(sysfs_create_bin_file(g_pKoRtc, &g_tivaRtcUnTsBin64))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
+		if(sysfs_create_file(g_pKoRtc, &g_tivaRtcCtrl.attr))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
+		if(sysfs_create_file(g_pKoRtc, &g_tivaRtcType.attr))
+		{
+			KALERT("sysfs_create_file failed\n");
+			break;
+		}
+
 		/////////////////////////////////////////////////////////////////////
 
 		if(!(g_pwq = create_workqueue("GfaWrk")))
@@ -752,7 +838,7 @@ static int drv_init(void)
 	while(0);
 
 	/////////////////////////////////////////////////////////////////////////
-	
+
 	_SetSysFsRunning(false);
 
 	if(_GetTimerRunning())
@@ -769,6 +855,26 @@ static int drv_init(void)
 
 	SfAttExit();
 
+	if(g_pKoRtc)
+	{
+		sysfs_remove_file(g_pKoRtc, &g_tivaRtcType.attr);
+		sysfs_remove_file(g_pKoRtc, &g_tivaRtcCtrl.attr);
+		sysfs_remove_bin_file(g_pKoRtc, &g_tivaRtcUnTsBin64);
+		sysfs_remove_file(g_pKoRtc, &g_tivaRtcIso8601.attr);
+
+		kobject_put(g_pKoRtc);
+		g_pKoRtc = NULL;
+	}
+
+	if(g_pKoMem)
+	{
+		sysfs_remove_file(g_pKoMem, &g_tivaMemMap.attr);
+		sysfs_remove_bin_file(g_pKoMem, &g_tivaMemory);
+
+		kobject_put(g_pKoMem);
+		g_pKoMem = NULL;
+	}
+
 	if(g_pKoFirmware)
 	{
 		sysfs_remove_bin_file(g_pKoFirmware, &g_tivaFwImageAtt);
@@ -794,8 +900,8 @@ static int drv_init(void)
 
 	if(g_pKoBacklight)
 	{
-		sysfs_remove_file(g_pKoBacklight, &g_tivaBrightnessAtt.attr);
-		sysfs_remove_file(g_pKoBacklight, &g_tivaFrequencyAtt.attr);
+		sysfs_remove_file(g_pKoBacklight, &g_tivaDutyCycleAtt.attr);
+		sysfs_remove_file(g_pKoBacklight, &g_tivaPeriodAtt.attr);
 
 		kobject_put(g_pKoBacklight);
 		g_pKoBacklight = NULL;
@@ -804,6 +910,7 @@ static int drv_init(void)
 	if(g_pKoTiva)
 	{
 		sysfs_remove_file(g_pKoTiva, &g_tivaUptimeAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaDevIdAtt.attr);
 
 		kobject_put(g_pKoTiva);
 		g_pKoTiva = NULL;
@@ -838,6 +945,26 @@ static void drv_exit(void)
 
 	SfAttExit();
 
+	if(g_pKoRtc)
+	{
+		sysfs_remove_file(g_pKoRtc, &g_tivaRtcType.attr);
+		sysfs_remove_file(g_pKoRtc, &g_tivaRtcCtrl.attr);
+		sysfs_remove_bin_file(g_pKoRtc, &g_tivaRtcUnTsBin64);
+		sysfs_remove_file(g_pKoRtc, &g_tivaRtcIso8601.attr);
+
+		kobject_put(g_pKoRtc);
+		g_pKoRtc = NULL;
+	}
+
+	if(g_pKoMem)
+	{
+		sysfs_remove_file(g_pKoMem, &g_tivaMemMap.attr);
+		sysfs_remove_bin_file(g_pKoMem, &g_tivaMemory);
+
+		kobject_put(g_pKoMem);
+		g_pKoMem = NULL;
+	}
+
 	if(g_pKoFirmware)
 	{
 		sysfs_remove_bin_file(g_pKoFirmware, &g_tivaFwImageAtt);
@@ -863,8 +990,8 @@ static void drv_exit(void)
 
 	if(g_pKoBacklight)
 	{
-		sysfs_remove_file(g_pKoBacklight, &g_tivaBrightnessAtt.attr);
-		sysfs_remove_file(g_pKoBacklight, &g_tivaFrequencyAtt.attr);
+		sysfs_remove_file(g_pKoBacklight, &g_tivaDutyCycleAtt.attr);
+		sysfs_remove_file(g_pKoBacklight, &g_tivaPeriodAtt.attr);
 
 		kobject_put(g_pKoBacklight);
 		g_pKoBacklight = NULL;
@@ -873,8 +1000,10 @@ static void drv_exit(void)
 	if(g_pKoTiva)
 	{
 		sysfs_remove_file(g_pKoTiva, &g_tivaUptimeAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaDevIdAtt.attr);
 
 		kobject_put(g_pKoTiva);
+		g_pKoTiva = NULL;
 	}
 
 	if(g_pKoGfa)

+ 23 - 0
kfile.c

@@ -2,6 +2,9 @@
 #include <linux/syscalls.h>
 #include "defines.h"
 #include "kfile.h"
+#include "ksync.h"
+
+static unsigned int g_nLocks = 0;
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -45,10 +48,30 @@ struct file* kf_open(const char *path, int flags, int rights)
 
 /////////////////////////////////////////////////////////////////////////////
 
+struct file* kf_open_locked(const char *path, int flags, int rights)
+{
+	struct file *pf = NULL;
+
+	kfile_lock();
+	if(!(pf = kf_open(path, flags, rights)))
+		kfile_unlock();
+	++g_nLocks;
+	return pf;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 void kf_close(struct file *pf)
 {
 	if(pf)
+	{
     	filp_close(pf, NULL);
+    	if(g_nLocks > 0)
+    	{
+    		--g_nLocks;
+			kfile_unlock();
+    	}
+    }
 }
 
 /////////////////////////////////////////////////////////////////////////////

+ 1 - 0
kfile.h

@@ -16,6 +16,7 @@ extern "C" {
 // kofile.h - Declarations:
 
 struct file*	kf_open(const char *path, int flags, int rights);
+struct file*	kf_open_locked(const char *path, int flags, int rights);
 void			kf_close(struct file *pf);
 int				kf_read(struct file *pf, unsigned long long offset, unsigned char *data, unsigned int size);
 int				kf_write(struct file *pf, unsigned long long offset, unsigned char *data, unsigned int size);

+ 527 - 0
krtc.c

@@ -0,0 +1,527 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include "ktiva.h"
+#include "ksync.h"
+#include "kfile.h"
+#include "krtc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _BCD2BIN(b)							((int)(((b) >> 4) * 10 + ((b) & 0x0F)))
+#define _BIN2BCD(b)							((unsigned char)(((b) / 10) << 4 | ((b) % 10)))
+
+#define _IS_12_HOUR_FORMAT_FLAG				(unsigned char)(1 << 6)
+#define _IS_PM_FLAG							(unsigned char)(1 << 5)
+
+#define _IS_12_HOUR_FORMAT(reg)				!!((reg) & _IS_12_HOUR_FORMAT_FLAG)
+#define _IS_PM(reg)							!!((reg) & _IS_PM_FLAG)
+
+#define _RTC_STOP_YIELD_DELAY				10
+#define _RTC_STOP_MAX_YIELD_DELAY			1000
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static RTCTypes g_rtcType = RTCT_Unknown;
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// https://ww1.microchip.com/downloads/en/devicedoc/20005010f.pdf
+// MCP7940
+
+#define _I2C_SLV_ADDR_MCP7940				0x6f
+#define _IS_MCP7940_OSC_RUN(reg)			(!!(reg & 0x20))	// OSCRUN:	1 = Oscillator is enabled and running
+																// 			0 = Oscillator has stopped or has been disabled
+#define _MCP7940_SRAM_ADDRESS				0x20
+#define _MCP7940_SRAM_SIZE					64
+#define _MCP7940_TEST_I2C_BUFFER_SIZE		28
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _MCP7940_get_date_time(struct file *pf, struct tm *ptm)
+{
+    int ret;
+    unsigned char rtc[7];
+
+	if(!(ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_MCP7940, 0, sizeof(rtc), rtc, sizeof(rtc))))
+	{
+		if(_IS_MCP7940_OSC_RUN(rtc[3]))
+		{
+			memset(ptm, 0, sizeof(struct tm));
+
+			ptm->tm_year	= _BCD2BIN(rtc[6]) + 100;
+			ptm->tm_mon		= _BCD2BIN(rtc[5] & 0x1F) - 1;
+			ptm->tm_mday	= _BCD2BIN(rtc[4] & 0x3F);
+			if(_IS_12_HOUR_FORMAT(rtc[2]))	// 12h format
+				ptm->tm_hour = _BCD2BIN(rtc[2] & 0x1F) + (_IS_PM(rtc[2]) ? 12 : 0);
+			else	// 24h format (default)
+				ptm->tm_hour = _BCD2BIN(rtc[2] & 0x3F);
+			ptm->tm_min		= _BCD2BIN(rtc[1] & 0x7F);
+			ptm->tm_sec		= _BCD2BIN(rtc[0] & 0x7F);
+			ret = 0;
+		}
+		else
+		{
+			KALERT("%s: Oscillator not running!\n", __FUNCTION__);
+			ret = -EIO;
+		}
+	}
+	else
+	{
+		KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+		ret = -EIO;
+	}
+	
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _MCP7940_set_date_time(struct file *pf, const struct tm *ptm)
+{
+	bool bOscRun;
+	unsigned int nMsSleep = 0;
+    int ret, y, m;
+    unsigned char rtc[9], reg = 0;
+
+	/////////////////////////////////////////////////////////////////////////
+	// Initiate a stop of the Oscillator by clearing register 0.
+
+    if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, 0, &reg, 1)))
+    {
+		KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+	/////////////////////////////////////////////////////////////////////////
+	// Meanwhile prepare the clock data that will be written to the RTC registers
+
+	KALERT("Setting RTC to %04ld-%02d-%02dT%02d:%02d:%02d UTC\n", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+
+    y = ptm->tm_year - 100;
+    m = ptm->tm_mon + 1;
+    
+    rtc[0] = _BIN2BCD(ptm->tm_sec) | 0x80;	// Write seconds and start Oscillator. This register will be written seperately at last!
+    rtc[1] = _BIN2BCD(ptm->tm_min);
+    rtc[2] = _BIN2BCD(ptm->tm_hour);		// uses 24h format
+    rtc[3] = (ptm->tm_wday + 1) | 0x08;		// MCP7940 day of week is in range 1-7, struct tm is in range 0-6 (start with Sunday).
+    										// Setting VBATEN (0x08) enables external battery backup supply.
+    rtc[4] = _BIN2BCD(ptm->tm_mday);
+    rtc[5] = _BIN2BCD(m);
+    rtc[6] = _BIN2BCD(y);
+    rtc[7] = 0x80;	// Set OUT bit in control register.
+    rtc[8] = 0;		// Clear OSCTRIM explicitly. Disables  Digital Trimming.
+
+	/////////////////////////////////////////////////////////////////////////
+	// Wait for the oscillator to stop. This is indicated by OSCRUN = 0 in register 3.
+
+    do
+    {
+		if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_MCP7940, 3, 1, &reg, 1))) // read register 3
+	    {
+			KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+			return -EIO;
+	    }
+	    
+	    if((bOscRun = _IS_MCP7940_OSC_RUN(reg)))		// if oscillator has not stopped yet
+	    {
+		    ksync_sleep_ms(_RTC_STOP_YIELD_DELAY);		// yield to another thread for _RTC_STOP_YIELD_DELAY ms
+		    nMsSleep += _RTC_STOP_YIELD_DELAY;			// and sum up the delay to the total yield time.
+		    
+		    if(nMsSleep >= _RTC_STOP_MAX_YIELD_DELAY)	// if the total yield time has reached _RTC_STOP_MAX_YIELD_DELAY, return a timeout error.
+		    {
+				KALERT("%s: Timeout while trying to stop RTC Oscillator!\n", __FUNCTION__);
+				return -ETIMEDOUT;
+		    }
+		}
+    }
+    while(bOscRun);
+
+	/////////////////////////////////////////////////////////////////////////
+	// write registers 1-8 (Minutes to Year and control/status and trim registers)
+
+    if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, 1, &rtc[1], sizeof(rtc) - 1)))
+    {
+		KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+	/////////////////////////////////////////////////////////////////////////
+	// write register 0 (Seconds and Start oscillator)
+
+    if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, 0, rtc, 1)))
+    {
+		KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+	return 0;	// RTC should now be set and running
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _MCP7940_test_i2c(struct file *pf)
+{
+	size_t i;
+    int ret;
+    unsigned char write[_MCP7940_TEST_I2C_BUFFER_SIZE], read[_MCP7940_TEST_I2C_BUFFER_SIZE];
+
+    for(i = 0; i < sizeof(write); ++i)
+    {
+    	write[i] = i;
+    	read[i] = 0xFF;
+    }
+
+    if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, _MCP7940_SRAM_ADDRESS, write, sizeof(write))))
+    {
+		KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+    if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_MCP7940, _MCP7940_SRAM_ADDRESS, sizeof(read), read, sizeof(read))))
+    {
+		KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+	if((ret = memcmp(write, read, sizeof(read))))
+	{
+		KALERT("%s: R/W Data mismatch!\n", __FUNCTION__);
+		ret = -EPROTO;
+	}
+
+    return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// https://datasheets.maximintegrated.com/en/ds/DS3231-DS3231S.pdf
+// DS3231
+
+#define _I2C_SLV_ADDR_DS3231				0x68
+#define _IS_DS3231_OSC_RUN(reg)				(!(reg & 0x80))	// OSF (Oscillator Stop Flag):
+															// 1 indicates that the oscillator either is stopped or was stopped for some period
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _DS3231_get_date_time(struct file *pf, struct tm *ptm)
+{
+    int ret;
+    unsigned char rtc[16];
+
+	if(!(ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_DS3231, 0, sizeof(rtc), rtc, sizeof(rtc))))
+	{
+		if(_IS_DS3231_OSC_RUN(rtc[15]))
+		{
+			memset(ptm, 0, sizeof(struct tm));
+
+			ptm->tm_year	= _BCD2BIN(rtc[6]) + 100;
+			ptm->tm_mon		= _BCD2BIN(rtc[5] & 0x1F) - 1;
+			ptm->tm_mday	= _BCD2BIN(rtc[4] & 0x3F);
+			if(_IS_12_HOUR_FORMAT(rtc[2]))	// 12h format
+				ptm->tm_hour = _BCD2BIN(rtc[2] & 0x1F) + (_IS_PM(rtc[2]) ? 12 : 0);
+			else	// 24h format (default)
+				ptm->tm_hour = _BCD2BIN(rtc[2] & 0x3F);
+			ptm->tm_min		= _BCD2BIN(rtc[1] & 0x7F);
+			ptm->tm_sec		= _BCD2BIN(rtc[0] & 0x7F);
+			ret = 0;
+		}
+		else
+		{
+			KALERT("%s: Oscillator not running!\n", __FUNCTION__);
+			ret = -EIO;
+		}
+	}
+	else
+	{
+		KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+		ret = -EIO;
+	}
+	
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _DS3231_set_date_time(struct file *pf, const struct tm *ptm)
+{
+    int ret, y, m;
+    unsigned char rtc[7], reg = 0;
+
+	/////////////////////////////////////////////////////////////////////////
+	// Prepare the clock data to be written to the RTC registers
+
+	KALERT("Setting RTC to %04ld-%02d-%02dT%02d:%02d:%02d UTC\n", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+
+    y = ptm->tm_year - 100;
+    m = ptm->tm_mon + 1;
+    
+    rtc[0] = _BIN2BCD(ptm->tm_sec);		// seconds
+    rtc[1] = _BIN2BCD(ptm->tm_min);		// minutes
+    rtc[2] = _BIN2BCD(ptm->tm_hour);	// hour, uses 24h format
+    rtc[3] = (ptm->tm_wday + 1);		// DS3231 day of week is in range 1-7, struct tm is in range 0-6 (start with Sunday).
+    rtc[4] = _BIN2BCD(ptm->tm_mday);	// day
+    rtc[5] = _BIN2BCD(m);				// month
+    rtc[6] = _BIN2BCD(y);				// year
+
+	/////////////////////////////////////////////////////////////////////////
+	// write registers 0-6 (Seconds to Year)
+	// The countdown chain is reset whenever the seconds register is written (0h).  Once the countdown chain is
+	// reset, to avoid rollover issues the remaining time and date registers must be written within 1 second.
+
+    if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_DS3231, 0, rtc, sizeof(rtc))))
+    {
+		KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+	/////////////////////////////////////////////////////////////////////////
+	// read/write register 15 (Status Register), to clear the OSF flag.
+
+	if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_DS3231, 15, 1, &reg, 1))) // read register 15
+    {
+    	reg &= 0x7F;	// clear OSF bit.
+
+	    if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_DS3231, 15, &reg, 1))) // write register 15
+	    {
+			KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
+			return -EIO;
+	    }
+	}
+	else
+    {
+		KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+		return -EIO;
+    }
+
+	return 0;	// RTC should now be set and running
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+int krtc_init(void)
+{
+    int i, ret = 0;
+	struct file *pfSpiDev = NULL;
+	g_rtcType = RTCT_Unknown;
+
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
+	{
+		if(KSpiInit(pfSpiDev))
+		{
+		    unsigned char rtc[7];
+
+			if(!(ret = TivaCmdGetI2C(pfSpiDev, _I2C_SLV_ADDR_MCP7940, 0, sizeof(rtc), rtc, sizeof(rtc)))) // MCP7940
+			{
+				for(i = 0; i < 7; ++i)
+				{
+					if(rtc[i] != 0xFF)
+					{
+						g_rtcType = RTCT_MCP7940;
+						break;
+					}
+				}
+				
+				if(g_rtcType == RTCT_Unknown)
+				{
+					if(!(ret = TivaCmdGetI2C(pfSpiDev, _I2C_SLV_ADDR_DS3231, 0, sizeof(rtc), rtc, sizeof(rtc)))) // DS3231
+					{
+						for(i = 0; i < 7; ++i)
+						{
+							if(rtc[i] != 0xFF)
+							{
+								g_rtcType = RTCT_DS3231;
+								break;
+							}
+						}
+					}
+					else
+					{
+						KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+						ret = -EIO;
+					}
+				}
+			}
+			else
+			{
+				KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
+				ret = -EIO;
+			}
+		}
+		else
+		{
+			KALERT("%s: KSpiInit failed\n", __FUNCTION__);
+			ret = -EIO;
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
+		ret = -EIO;
+	}
+
+	if(g_rtcType == RTCT_Unknown)
+	{
+		KALERT("%s: Unable to determine RTC-Type!\n", __FUNCTION__);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+RTCTypes krtc_get_type(void)
+{
+	return g_rtcType;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int krtc_get_date_time(struct tm *ptm)
+{
+    int ret;
+	struct file *pfSpiDev = NULL;
+    
+    if(!KRTC_IS_VALID_RTC_TYPE(g_rtcType))
+    {
+		KALERT("%s: Invalid RTC-Type!\n", __FUNCTION__);
+		return -ENODEV;
+    }
+
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
+	{
+		if(KSpiInit(pfSpiDev))
+		{
+			switch(g_rtcType)
+			{
+			case RTCT_MCP7940:
+				if((ret = _MCP7940_get_date_time(pfSpiDev, ptm)))
+					KALERT("%s: _MCP7940_get_date_time failed!\n", __FUNCTION__);
+				break;
+			case RTCT_DS3231:
+				if((ret = _DS3231_get_date_time(pfSpiDev, ptm)))
+					KALERT("%s: _DS3231_get_date_time failed!\n", __FUNCTION__);
+				break;
+			default:
+				break;
+			}
+		}
+		else
+		{
+			KALERT("%s: KSpiInit failed\n", __FUNCTION__);
+			ret = -EIO;
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int krtc_set_date_time(const struct tm *ptm)
+{
+    int ret;
+	struct file *pfSpiDev = NULL;
+//	unsigned long jStart = jiffies, jDiff;
+    
+    if(!KRTC_IS_VALID_RTC_TYPE(g_rtcType))
+    {
+		KALERT("%s: Invalid RTC-Type!\n", __FUNCTION__);
+		return -ENODEV;
+    }
+
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
+	{
+		if(KSpiInit(pfSpiDev))
+		{
+			switch(g_rtcType)
+			{
+			case RTCT_MCP7940:
+//				jDiff = jiffies - jStart;
+//				KALERT("%s: prep _MCP7940_set_date_time took %u us.\n", __FUNCTION__, jiffies_to_usecs(jDiff));
+//				jStart = jiffies;
+				if((ret = _MCP7940_set_date_time(pfSpiDev, ptm)))
+					KALERT("%s: _MCP7940_set_date_time failed!\n", __FUNCTION__);
+//				jDiff = jiffies - jStart;
+//				KALERT("%s: call _MCP7940_set_date_time took %u us.\n", __FUNCTION__, jiffies_to_usecs(jDiff));
+				break;
+			case RTCT_DS3231:
+				if((ret = _DS3231_set_date_time(pfSpiDev, ptm)))
+					KALERT("%s: _DS3231_set_date_time failed!\n", __FUNCTION__);
+				break;
+			default:
+				break;
+			}
+		}
+		else
+		{
+			KALERT("%s: KSpiInit failed\n", __FUNCTION__);
+			ret = -EIO;
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int krtc_test_i2c(void)
+{
+    int ret;
+	struct file *pfSpiDev = NULL;
+    
+    if(g_rtcType != RTCT_MCP7940)
+    {
+    	if(g_rtcType == RTCT_DS3231)
+    	{
+			KALERT("%s: I2C-Test not implemented in DS3231!\n", __FUNCTION__);
+			return -ENOTSUPP;
+    	}
+    	else
+    	{
+			KALERT("%s: Invalid RTC-Type!\n", __FUNCTION__);
+			return -ENODEV;
+    	}
+    }
+
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
+	{
+		if(KSpiInit(pfSpiDev))
+		{
+			if((ret = _MCP7940_test_i2c(pfSpiDev)))
+				KALERT("%s: _MCP7940_test_i2c failed!\n", __FUNCTION__);
+		}
+		else
+		{
+			KALERT("%s: KSpiInit failed\n", __FUNCTION__);
+			ret = -EIO;
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}

+ 33 - 0
krtc.h

@@ -0,0 +1,33 @@
+// krtc.h :
+//
+
+#if !defined(AGD_KRTC_H__B461A196_A386_48D9_973F_7F8F085072E7__INCLUDED_)
+#define AGD_KRTC_H__B461A196_A386_48D9_973F_7F8F085072E7__INCLUDED_
+
+#include "defines.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// krtc.h - Declarations:
+
+typedef enum
+{
+	RTCT_Unknown,
+	RTCT_MCP7940,	// standard
+	RTCT_DS3231
+}RTCTypes;
+
+#define KRTC_IS_VALID_RTC_TYPE(t)			(((t) == RTCT_MCP7940) || ((t) == RTCT_DS3231))
+
+#define KRTC_TIMESTAMP_2000_01_01			 946684800LL
+#define KRTC_TIMESTAMP_2100_01_01			4102444800LL
+
+/////////////////////////////////////////////////////////////////////////////
+
+int krtc_init(void);
+RTCTypes krtc_get_type(void);
+int krtc_get_date_time(struct tm *ptm);
+int krtc_set_date_time(const struct tm *ptm);
+int krtc_test_i2c(void);
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_KRTC_H__B461A196_A386_48D9_973F_7F8F085072E7__INCLUDED_)

+ 28 - 9
kspi.c

@@ -3,6 +3,7 @@
 #include "kspi.h"
 #include "kfile.h"
 #include "ktiva.h"
+#include "ksync.h"
 
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
@@ -11,7 +12,9 @@ int kspi_write_mode(struct file *pf, unsigned char mode)
 {
 	int ret;
 
-	if((ret = kf_ioctl(pf, SPI_IOC_WR_MODE, (unsigned long)&mode)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_WR_MODE, (unsigned long)&mode);
+	
+	if(ret < 0)
 	{
 		KALERT("%s: kf_ioctl failed: %d\n", __FUNCTION__, ret);
 	}
@@ -25,7 +28,9 @@ int kspi_read_mode(struct file *pf, unsigned char *mode)
 {
 	int ret;
 
-	if((ret = kf_ioctl(pf, SPI_IOC_RD_MODE, (unsigned long)mode)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_RD_MODE, (unsigned long)mode);
+
+	if(ret < 0)
 	{
 		KALERT("%s: kf_ioctl failed: %d\n", __FUNCTION__, ret);
 	}
@@ -39,7 +44,9 @@ int kspi_write_bits_per_word(struct file *pf, unsigned char bits)
 {
 	int ret;
 
-	if((ret = kf_ioctl(pf, SPI_IOC_WR_BITS_PER_WORD, (unsigned long)&bits)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_WR_BITS_PER_WORD, (unsigned long)&bits);
+
+	if(ret < 0)
 	{
 		KALERT("%s: kf_ioctl failed: %d\n", __FUNCTION__, ret);
 	}
@@ -53,7 +60,9 @@ int kspi_read_bits_per_word(struct file *pf, unsigned char *bits)
 {
 	int ret;
 
-	if((ret = kf_ioctl(pf, SPI_IOC_RD_BITS_PER_WORD, (unsigned long)bits)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_RD_BITS_PER_WORD, (unsigned long)bits);
+
+	if(ret < 0)
 	{
 		KALERT("%s: kf_ioctl failed: %d\n", __FUNCTION__, ret);
 	}
@@ -67,7 +76,9 @@ int kspi_write_max_speed_hz(struct file *pf, unsigned int speed)
 {
 	int ret;
 
-	if((ret = kf_ioctl(pf, SPI_IOC_WR_MAX_SPEED_HZ, (unsigned long)&speed)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_WR_MAX_SPEED_HZ, (unsigned long)&speed);
+
+	if(ret < 0)
 	{
 		KALERT("%s: kf_ioctl failed: %d\n", __FUNCTION__, ret);
 	}
@@ -81,7 +92,9 @@ int kspi_read_max_speed_hz(struct file *pf, unsigned int *speed)
 {
 	int ret;
 
-	if((ret = kf_ioctl(pf, SPI_IOC_RD_MAX_SPEED_HZ, (unsigned long)speed)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_RD_MAX_SPEED_HZ, (unsigned long)speed);
+
+	if(ret < 0)
 	{
 		KALERT("%s: kf_ioctl failed: %d\n", __FUNCTION__, ret);
 	}
@@ -107,7 +120,9 @@ int kspi_tx(struct file *pf, const void *pData, size_t nCbData)
 //		.bits_per_word	= _SPI_BITS_PER_WORD,
 	};
 
-	if((ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr);
+
+	if(ret < 0)
 		KALERT("%s failed: %d\n", __FUNCTION__, ret);
     return ret;
 }
@@ -128,7 +143,9 @@ int kspi_rx(struct file *pf, void *pData, size_t nCbData)
 //		.bits_per_word	= _SPI_BITS_PER_WORD,
 	};
 
-	if((ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr);
+
+	if(ret < 0)
 		KALERT("%s failed: %d\n", __FUNCTION__, ret);
     return ret;
 }
@@ -149,7 +166,9 @@ int kspi_tx_rx(struct file *pf, const void *pTx, void *pRx, size_t nCb)
 //		.bits_per_word	= _SPI_BITS_PER_WORD,
 	};
 
-	if((ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr)) < 0)
+	ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr);
+
+	if(ret < 0)
 		KALERT("%s failed: %d\n", __FUNCTION__, ret);
     return ret;
 }

+ 18 - 3
ksync.c

@@ -10,20 +10,21 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-static DEFINE_MUTEX(g_mutex);
+static DEFINE_MUTEX(g_mtxSync);
+static DEFINE_MUTEX(g_mtxSPI);
 
 /////////////////////////////////////////////////////////////////////////////
 
 void ksync_lock(void)
 {
-	mutex_lock(&g_mutex);
+	mutex_lock(&g_mtxSync);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
 void ksync_unlock(void)
 {
-	mutex_unlock(&g_mutex);
+	mutex_unlock(&g_mtxSync);
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -42,3 +43,17 @@ int ksync_sleep_ms(long ms)
 {
 	return ksync_sleep_jiffies(msecs_to_jiffies(ms));
 }
+
+/////////////////////////////////////////////////////////////////////////////
+
+void kfile_lock(void)
+{
+	mutex_lock(&g_mtxSPI);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void kfile_unlock(void)
+{
+	mutex_unlock(&g_mtxSPI);
+}

+ 2 - 0
ksync.h

@@ -15,6 +15,8 @@ void ksync_lock(void);
 void ksync_unlock(void);
 int ksync_sleep_jiffies(long jiffies);
 int ksync_sleep_ms(long ms);
+void kfile_lock(void);
+void kfile_unlock(void);
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus

+ 171 - 5
ktiva.c

@@ -5,6 +5,11 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
+#define _SQUARE_COEFFICIENT		6
+#define _LINEAR_COEFFICIENT		400
+
+/////////////////////////////////////////////////////////////////////////////
+
 extern int g_sw;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -50,7 +55,33 @@ static int _lin_kty(int resistance)
 
 /////////////////////////////////////////////////////////////////////////////
 
-static int _scale (int in_min, int in_max, int out_min, int out_max, int wert)
+#if _SCALE_DISPLAY_DUTY_CYCLE
+
+static unsigned int _ScaleDutyCycle(unsigned int nPeriod, unsigned int nDutyCyclePerc)
+{
+	if(nDutyCyclePerc < 0)
+		nDutyCyclePerc = 0;
+	else if(nDutyCyclePerc > 100)
+		nDutyCyclePerc = 100;
+	return (_SQUARE_COEFFICIENT * nDutyCyclePerc * nDutyCyclePerc + _LINEAR_COEFFICIENT * nDutyCyclePerc) / 1000 * nPeriod / 100;
+}
+
+#else	//	_SCALE_DISPLAY_DUTY_CYCLE
+
+static unsigned int _ScaleDutyCycle(unsigned int nPeriod, unsigned int nDutyCyclePerc)
+{
+	if(nDutyCyclePerc < 0)
+		nDutyCyclePerc = 0;
+	else if(nDutyCyclePerc > 100)
+		nDutyCyclePerc = 100;
+	return nDutyCyclePerc * nPeriod / 100;
+}
+
+#endif	//	_SCALE_DISPLAY_DUTY_CYCLE
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _scale(int in_min, int in_max, int out_min, int out_max, int wert)
 {
 	int abc;
 	abc = (((long)out_max - (long)out_min) * (long)wert) / ( (long)in_max - (long)in_min);
@@ -148,7 +179,6 @@ int ktiva_recv_frame(struct file *pf, unsigned char cmd, void *pData, size_t nCb
 	int ret, i;
 	unsigned char chk = 0, len = 0;
 	unsigned char buf[KTIVA_MAX_BUFFER_SIZE];
-//    unsigned long start = jiffies;
     unsigned long timeout = jiffies + HZ; // 1 sec. timeout
 
 #if _EXTENDED_ERROR_CHECK
@@ -180,7 +210,7 @@ int ktiva_recv_frame(struct file *pf, unsigned char cmd, void *pData, size_t nCb
 	if(len > (int)(nCbData + 3))
 	{
 		KALERT("%s: Insufficient buffer length: %zu - need %hhu!\n", __FUNCTION__, nCbData, len - 3);
-		return -ENOMEM;
+		return -EINVAL;
 	}
 	else if(len < 2)
 	{
@@ -342,6 +372,35 @@ int TivaRevive(struct file *pf)
 
 /////////////////////////////////////////////////////////////////////////////
 
+int TivaCmdGetDeviceCaps(struct file *pf, LPTIVA_DCAP pDCap)
+{
+    int ret;
+	unsigned int did0 = 0xFFFFFFFF, did1 = 0xFFFFFFFF, dc0 = 0xFFFFFFFF;
+
+	if((ret = TivaCmdGetAddress(pf, KTIVA_DID0_ADDRESS, &did0)))
+	{
+		KALERT("%s - TivaCmdGetAddress failed: %d\n", __FUNCTION__, ret);
+		return ret;
+	}
+	else if((ret = TivaCmdGetAddress(pf, KTIVA_DID1_ADDRESS, &did1)))
+	{
+		KALERT("%s - TivaCmdGetAddress failed: %d\n", __FUNCTION__, ret);
+		return ret;
+	}
+	else if((ret = TivaCmdGetAddress(pf, KTIVA_DCAP0_ADDRESS, &dc0)))
+	{
+		KALERT("%s - TivaCmdGetAddress failed: %d\n", __FUNCTION__, ret);
+		return ret;
+	}
+	
+	pDCap->did0 = did0;
+	pDCap->did1 = did1;
+	pDCap->dc0 = dc0;
+	return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 int TivaCmdGetFirmwareVersion(struct file *pf, int *Hw, int *Sw)
 {
     int ret;
@@ -505,6 +564,27 @@ int TivaCmdGetAddress(struct file *pf, unsigned int addr, unsigned int *val)
 
 /////////////////////////////////////////////////////////////////////////////
 
+int TivaCmdSetAddress(struct file *pf, unsigned int addr, unsigned int val)
+{
+    int ret;
+    unsigned int data[2] = {cpu_to_be32(addr), cpu_to_be32(val)};
+
+    if((ret = ktiva_send_frame(pf, COMMAND_SET_ADDR, &data, sizeof(data))) != sizeof(data))
+	{
+		KALERT("%s - ktiva_send_frame failed: %d!\n", __FUNCTION__, ret);
+		if(ret > 0)
+	    	ret = -1;
+	}
+	else
+	{
+		ret = 0;
+	}
+
+    return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 int TivaCmdPing(struct file *pf)
 {
     int ret;
@@ -694,10 +774,14 @@ int TivaCmdGetMatSer(struct file *pf, LPTIVA_MAT_SER pms)
 
 /////////////////////////////////////////////////////////////////////////////
 
-int TivaCmdSetBacklight(struct file *pf, unsigned int nFrequency, unsigned int nBrightness)
+int TivaCmdSetBacklight(struct file *pf, unsigned int nPeriod, unsigned int nDutyCyclePerc)
 {
     int ret;
-    unsigned int data[2] = {cpu_to_be32(nFrequency), cpu_to_be32(nBrightness * 10)};
+    unsigned int data[2] =
+    {
+    	cpu_to_be32(nPeriod),
+    	cpu_to_be32(_ScaleDutyCycle(nPeriod, nDutyCyclePerc))
+    };
 
     if((ret = ktiva_send_frame(pf, COMMAND_SET_BACKLIGHT, data, sizeof(data))) == sizeof(data))
     {
@@ -710,3 +794,85 @@ int TivaCmdSetBacklight(struct file *pf, unsigned int nFrequency, unsigned int n
 
     return ret;
 }
+
+/////////////////////////////////////////////////////////////////////////////
+
+int TivaCmdGetI2C(struct file *pf, unsigned char nI2CAddr, unsigned char nI2CStart, size_t nCbI2C, void *pData, size_t nCbData)
+{
+    int ret;
+	unsigned char data[3] =
+	{
+		nI2CAddr,
+		nI2CStart,
+		(unsigned char)nCbI2C
+	};
+
+#if _EXTENDED_ERROR_CHECK
+	if(!pData)
+	{
+		KALERT("%s: Invalid data pointer!\n", __FUNCTION__);
+		return -EINVAL;
+	}
+#endif	//	_EXTENDED_ERROR_CHECK
+
+    if((ret = ktiva_send_frame(pf, COMMAND_GET_I2C, data, sizeof(data))) == sizeof(data))
+    {
+    	if((ret = ktiva_recv_frame(pf, COMMAND_GET_I2C, pData, nCbData)) == nCbData)
+    	{
+			kspi_tx_byte(pf, CMD_ACK);
+            ret = 0;
+    	}
+	    else
+	    {
+			KALERT("%s - ktiva_recv_frame failed: %d!\n", __FUNCTION__, ret);
+	    }
+    }
+    else
+    {
+		KALERT("%s - ktiva_send_frame failed: %d!\n", __FUNCTION__, ret);
+    }
+
+    return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int TivaCmdSetI2C(struct file *pf, unsigned char nI2CAddr, unsigned char nI2CStart, const void *pData, size_t nCbData)
+{
+    int ret;
+    size_t nLen = 3;
+	unsigned char data[512] =
+	{
+		nI2CAddr,
+		nI2CStart,
+		(unsigned char)nCbData
+	};
+
+#if _EXTENDED_ERROR_CHECK
+	if(!pData)
+	{
+		KALERT("%s: Invalid data pointer!\n", __FUNCTION__);
+		return -EINVAL;
+	}
+#endif	//	_EXTENDED_ERROR_CHECK
+
+	if((nLen + nCbData) > sizeof(data))
+	{
+		KALERT("%s: Invalid data size: %zu!\n", __FUNCTION__, nCbData);
+		return -EINVAL;
+	}
+	
+	memcpy(&data[3], pData, nCbData);
+	nLen += nCbData;
+
+    if((ret = ktiva_send_frame(pf, COMMAND_PUT_I2C, data, nLen)) == nLen)
+    {
+		ret = 0;
+    }
+    else
+    {
+		KALERT("%s - ktiva_send_frame failed: %d!\n", __FUNCTION__, ret);
+    }
+
+    return ret;
+}

+ 23 - 1
ktiva.h

@@ -52,6 +52,24 @@ extern "C" {
 #define KTIVA_MAX_WAIT_LOOPS						100
 #define KTIVA_UPTIME_ADDRESS						0x200003C0
 
+#define KTIVA_DCAP_BASE_ADDRESS						0x400FE000
+#define KTIVA_DID0_ADDRESS							KTIVA_DCAP_BASE_ADDRESS
+#define KTIVA_DID1_ADDRESS							(KTIVA_DCAP_BASE_ADDRESS + 0x4)
+#define KTIVA_DCAP0_ADDRESS							(KTIVA_DCAP_BASE_ADDRESS + 0x8)
+
+#define KTIVA_FLASH_BASE_ADDRESS					((unsigned int)0x00000000)
+#define KTIVA_SRAM_BASE_ADDRESS						((unsigned int)0x20000000)
+#define KTIVA_SRAM_BIT_BAND_BASE_ADDRESS			((unsigned int)0x22000000)
+#define KTIVA_PERIPHERALS_BASE_ADDRESS				((unsigned int)0x40000000)
+#define KTIVA_PERIPHERALS_BIT_BAND_BASE_ADDRESS		((unsigned int)0x42000000)
+
+#define KTIVA_ROM_BASE_ADDRESS						((unsigned int)0x01000000)
+
+#define KTIVA_FLASH_SIZE(s)							(((unsigned int)(s) + 1) * 2048)
+#define KTIVA_SRAM_SIZE(s)							(((unsigned int)(s) + 1) * 256)
+#define KTIVA_PERIPHERALS_SIZE						((unsigned int)0x00100000)
+#define KTIVA_PERIPHERALS_BIT_BAND_SIZE				((unsigned int)0x02000000)
+
 /////////////////////////////////////////////////////////////////////////////
 
 int ktiva_wait_ack	(struct file *pf);
@@ -63,8 +81,10 @@ int ktiva_recv_frame(struct file *pf, unsigned char cmd, void *pData, size_t nCb
 int TivaCmdPing					(struct file *pf);
 int TivaCmdGetFirmwareVersion	(struct file *pf, int *Hw, int *Sw);
 int TivaCmdGetADC				(struct file *pf, LPTIVA_ADC padc);
+int TivaCmdGetDeviceCaps		(struct file *pf, LPTIVA_DCAP pDid);
 int TivaCmdGetUptime			(struct file *pf, unsigned long long *put);
 int TivaCmdGetAddress			(struct file *pf, unsigned int addr, unsigned int *val);
+int TivaCmdSetAddress			(struct file *pf, unsigned int addr, unsigned int val);
 int TivaCmdGetStatus			(struct file *pf, unsigned char *stat);
 int TivaCmdGetBootloaderStatus	(struct file *pf, unsigned char *stat);
 int TivaCmdStartBootloader		(struct file *pf);
@@ -73,7 +93,9 @@ int TivaCmdSendDataBlock		(struct file *pf, const void *pBlock, size_t nCbBlock)
 int TivaCmdReset				(struct file *pf);
 int TivaRevive					(struct file *pf);
 int TivaCmdGetMatSer			(struct file *pf, LPTIVA_MAT_SER pms);
-int TivaCmdSetBacklight			(struct file *pf, unsigned int nFrequency, unsigned int nBrightness);
+int TivaCmdSetBacklight			(struct file *pf, unsigned int nPeriod, unsigned int nDutyCyclePerc);
+int TivaCmdGetI2C				(struct file *pf, unsigned char nI2CAddr, unsigned char nI2CStart, size_t nCbI2C, void *pData, size_t nCbData);
+int TivaCmdSetI2C				(struct file *pf, unsigned char nI2CAddr, unsigned char nI2CStart, const void *pData, size_t nCbData);
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus

+ 517 - 37
sfsattrib.c

@@ -4,26 +4,124 @@
 #include <linux/module.h>
 #include <linux/kobject.h>
 #include <linux/errno.h>
+#include <linux/ctype.h>
 #include <linux/sysfs.h>
 #include <linux/syscalls.h>
 #include "defines.h"
 #include "sfsattrib.h"
 #include "kfirmware.h"
 #include "ksync.h"
+#include "kfile.h"
+#include "ktiva.h"
 
 /////////////////////////////////////////////////////////////////////////////
 
 int g_hw = -1, g_sw = -1;
 TIVA_ADC g_tadc;
 unsigned long long g_nUpTime = 0;
+TIVA_DCAP g_dcaps;
+
+TIVA_MEMORY_MAP g_tiMM[] =
+{
+	{	// Flash
+		.baseAddr = KTIVA_FLASH_BASE_ADDRESS,
+		.bIsRO = true
+	},
+	{	// SRAM
+		.baseAddr = KTIVA_SRAM_BASE_ADDRESS,
+		.bIsRO = false
+	},
+	{	// Bit banded SRAM
+		.baseAddr = KTIVA_SRAM_BIT_BAND_BASE_ADDRESS,
+		.bIsRO = false
+	},
+	{	// Peripherals
+		.baseAddr = KTIVA_PERIPHERALS_BASE_ADDRESS,
+		.size = KTIVA_PERIPHERALS_SIZE,
+		.bIsRO = false
+	},
+	{	// Bit banded Peripherals
+		.baseAddr = KTIVA_PERIPHERALS_BIT_BAND_BASE_ADDRESS,
+		.size = KTIVA_PERIPHERALS_BIT_BAND_SIZE,
+		.bIsRO = false
+	}
+};
 
 static void *g_pFwBuffer	= NULL;
 static size_t g_nCbFwData	= 0;
 
 static atomic_t g_flgFwLocked;
 static atomic_t g_flgBacklightChanged;
-static atomic_t g_valBacklightBrightness;
-static atomic_t g_valBacklightFrequency;
+static atomic_t g_valBacklightDutyCyclePercent;
+static atomic_t g_valBacklightPeriod;
+
+/////////////////////////////////////////////////////////////////////////////
+
+void time64_to_tm(time64_t totalsecs, int offset, struct tm *result);
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+bool SfAttInit(void)
+{
+	g_nCbFwData = 0;
+	atomic_set(&g_flgBacklightChanged, 0);
+	SfAttSetBacklightPeriod(_BACKLIGHT_PERIOD_DEFAULT);
+	SfAttSetBacklightDutyCyclePercent(_BACKLIGHT_DUTY_CYCLE_PERC_DEF);
+
+	if(krtc_init() < 0)
+	{
+		KALERT("%s: krtc_init failed!\n", __FUNCTION__);
+	}
+	
+	memset(&g_dcaps, 0xFF, sizeof(g_dcaps));
+
+	g_pFwBuffer = (void*)__get_free_pages(GFP_KERNEL, _FIRMWARE_PAGES_COUNT);
+	return !!g_pFwBuffer;
+}
+
+void SfAttExit(void)
+{
+	g_nCbFwData = 0;
+	if(g_pFwBuffer)
+	{
+		free_pages((unsigned long)g_pFwBuffer, _FIRMWARE_PAGES_COUNT);
+		g_pFwBuffer = NULL;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static bool _AddressReadable(unsigned int nAddr, unsigned int nLength)
+{
+	size_t i;
+	LPCTIVA_MEMORY_MAP pmm = g_tiMM;
+	for(i = 0; i < _countof(g_tiMM); ++i, ++pmm)
+	{
+		if(	(nAddr < pmm->baseAddr + pmm->size) &&
+			(nAddr >= pmm->baseAddr))
+		{
+			return (nAddr + nLength) <= pmm->baseAddr + pmm->size;
+		}
+	}
+	return false;
+}
+
+static bool _AddressWriteable(unsigned int nAddr, unsigned int nLength)
+{
+	size_t i;
+	LPCTIVA_MEMORY_MAP pmm = g_tiMM;
+	for(i = 0; i < _countof(g_tiMM); ++i, ++pmm)
+	{
+		if(	!pmm->bIsRO &&
+			(nAddr < pmm->baseAddr + pmm->size) &&
+			(nAddr >= pmm->baseAddr))
+		{
+			return (nAddr + nLength) <= pmm->baseAddr + pmm->size;
+		}
+	}
+	return false;
+}
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -39,6 +137,33 @@ static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, c
 
 /////////////////////////////////////////////////////////////////////////////
 
+static ssize_t did_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	TIVA_DCAP dcaps;
+	ksync_lock();
+	dcaps = g_dcaps;
+	ksync_unlock();
+	return sprintf(buf, "DID0: %08X\n" \
+						"DID1: %08X\n" \
+						"##############\n" \
+						"DID0Ver: %X\n" \
+						"Class: %X\n" \
+						"RevMaj: %X\n" \
+						"RevMin: %X\n" \
+						"##############\n" \
+						"DID1Ver: %X\n" \
+						"Family: %X\n" \
+						"PartNo: %X\n" \
+						"PinCnt: %X\n" \
+						"TempRange: %X\n" \
+						"PackageType: %X\n" \
+						"RoHSComp: %X\n" \
+						"QualStat: %X\n", \
+						dcaps.did0, dcaps.did1,
+						dcaps.ver0, dcaps.cls, dcaps.maj, dcaps.min,
+						dcaps.ver1, dcaps.fam, dcaps.partno, dcaps.pincnt, dcaps.temp, dcaps.pkg, dcaps.rohs, dcaps.qual);
+}
+
 static ssize_t uptime_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 {
 	unsigned long long nVal;
@@ -131,7 +256,7 @@ static ssize_t AdcBin_read(struct file *pf, struct kobject *kobj, struct bin_att
 
 /////////////////////////////////////////////////////////////////////////////
 
-static ssize_t brightness_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
+static ssize_t dutycycle_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
 {
 	ssize_t ret = -EINVAL;
 	if(buf && count)
@@ -144,7 +269,7 @@ static ssize_t brightness_store(struct kobject *kobj, struct kobj_attribute *att
 		szBuf[count] = '\0';
 		if(!(ret = kstrtoll(szBuf, 10, &val)))
 		{
-			SfAttSetBacklightBrightness(val);
+			SfAttSetBacklightDutyCyclePercent(val);
 			atomic_set(&g_flgBacklightChanged, 1);
 			ret = count;
 		}
@@ -154,7 +279,7 @@ static ssize_t brightness_store(struct kobject *kobj, struct kobj_attribute *att
 
 /////////////////////////////////////////////////////////////////////////////
 
-static ssize_t frequency_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
+static ssize_t period_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
 {
 	ssize_t ret = -EINVAL;
 	if(buf && count)
@@ -167,7 +292,7 @@ static ssize_t frequency_store(struct kobject *kobj, struct kobj_attribute *attr
 		szBuf[count] = '\0';
 		if(!(ret = kstrtoll(szBuf, 10, &val)))
 		{
-			SfAttSetBacklightFrequency(val);
+			SfAttSetBacklightPeriod(val);
 			atomic_set(&g_flgBacklightChanged, 1);
 			ret = count;
 		}
@@ -248,24 +373,364 @@ static ssize_t image_write(struct file *pf, struct kobject *kobj, struct bin_att
 	return len;
 }
 
-bool SfAttInit(void)
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t mem_read(struct file *pf, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t len)
 {
-	g_nCbFwData = 0;
-	atomic_set(&g_flgBacklightChanged, 0);
-	SfAttSetBacklightBrightness(_BACKLIGHT_DEF_BRIGHT_PERC);
-	SfAttSetBacklightFrequency(_BACKLIGHT_DEF_FREQ_HZ);
-	g_pFwBuffer = (void*)__get_free_pages(GFP_KERNEL, _FIRMWARE_PAGES_COUNT);
-	return !!g_pFwBuffer;
+    int ret;
+	unsigned int val;
+	struct file *pfSpiDev = NULL;
+
+	if(SfAttIsFirmwareLocked())
+		return -EBUSY;
+
+	if(off & 0x00000003)
+	{
+		KALERT("%s: Invalid address: %lld\n", __FUNCTION__, off);
+		return -EFAULT;
+	}
+
+	if(len > sizeof(val))
+		len = sizeof(val);
+	else if(len < sizeof(val))
+	{
+		KALERT("%s: Buffer too small: %zu\n", __FUNCTION__, len);
+		return -EINVAL;
+	}
+
+	if(!_AddressReadable(off, len))
+	{
+		KALERT("%s: Invalid address (0x%llX) or length (%zu)\n", __FUNCTION__, off, len);
+		return -EACCES;
+	}
+
+//	KALERT("%s: Try to read from 0x%08llX, length %zu Bytes.\n", __FUNCTION__, off, len);
+
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
+	{
+		if(KSpiInit(pfSpiDev))
+		{
+			if(!(ret = TivaCmdGetAddress(pfSpiDev, (unsigned int)off, &val)))
+			{
+				memcpy(buf, &val, len);
+				ret = (ssize_t)len;
+			}
+			else
+			{
+				KALERT("%s: TivaCmdGetAddress failed (%d)\n", __FUNCTION__, ret);
+				ret = -EIO;
+			}
+		}
+		else
+		{
+			KALERT("%s: KSpiInit failed\n", __FUNCTION__);
+			ret = -EIO;
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
+		ret = -EIO;
+	}
+
+	return ret;
 }
 
-void SfAttExit(void)
+static ssize_t mem_write(struct file *pf, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t len)
 {
-	g_nCbFwData = 0;
-	if(g_pFwBuffer)
+    int ret;
+	struct file *pfSpiDev = NULL;
+
+	if(SfAttIsFirmwareLocked())
+		return -EBUSY;
+		
+	if(off & 0x00000003)
 	{
-		free_pages((unsigned long)g_pFwBuffer, _FIRMWARE_PAGES_COUNT);
-		g_pFwBuffer = NULL;
+		KALERT("%s: Invalid address: %lld\n", __FUNCTION__, off);
+		return -EFAULT;
+	}
+	
+	if(len != sizeof(unsigned int))
+	{
+		KALERT("%s: Invalid buffer size: %zu (must be %zu)\n", __FUNCTION__, len, sizeof(unsigned int));
+		return -EINVAL;
 	}
+
+	if(!_AddressWriteable(off, len))
+	{
+		KALERT("%s: Invalid address (0x%llX) or length (%zu) or range is read-only!\n", __FUNCTION__, off, len);
+		return -EACCES;
+	}
+
+	if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
+	{
+		if(KSpiInit(pfSpiDev))
+		{
+			unsigned int val;
+			memcpy(&val, buf, sizeof(val));
+
+			if(!(ret = TivaCmdSetAddress(pfSpiDev, (unsigned int)off, val)))
+			{
+				ret = (ssize_t)len;
+			}
+			else
+			{
+				KALERT("%s: TivaCmdSetAddress failed (%d)\n", __FUNCTION__, ret);
+				ret = -EIO;
+			}
+		}
+		else
+		{
+			KALERT("%s: KSpiInit failed\n", __FUNCTION__);
+			ret = -EIO;
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static ssize_t map_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "On-chip FLASH:  %08X-%08X  %7X %s\n" \
+						"On-chip SRAM:   %08X-%08X  %7X %s\n" \
+						"BBA SRAM:       %08X-%08X  %7X %s\n" \
+						"Peripheral:     %08X-%08X  %7X %s\n" \
+						"BBA Peripheral: %08X-%08X  %7X %s\n",
+						g_tiMM[0].baseAddr, g_tiMM[0].baseAddr + g_tiMM[0].size - 1, g_tiMM[0].size, g_tiMM[0].bIsRO ? "RO" : "RW",
+						g_tiMM[1].baseAddr, g_tiMM[1].baseAddr + g_tiMM[1].size - 1, g_tiMM[1].size, g_tiMM[1].bIsRO ? "RO" : "RW",
+						g_tiMM[2].baseAddr, g_tiMM[2].baseAddr + g_tiMM[2].size - 1, g_tiMM[2].size, g_tiMM[2].bIsRO ? "RO" : "RW",
+						g_tiMM[3].baseAddr, g_tiMM[3].baseAddr + g_tiMM[3].size - 1, g_tiMM[3].size, g_tiMM[3].bIsRO ? "RO" : "RW",
+						g_tiMM[4].baseAddr, g_tiMM[4].baseAddr + g_tiMM[4].size - 1, g_tiMM[4].size, g_tiMM[4].bIsRO ? "RO" : "RW");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t iso8601_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret;
+	struct tm tm;
+
+	if(SfAttIsFirmwareLocked())
+	{
+		KALERT("%s: Firmware update in progress!\n", __FUNCTION__);
+		return -EBUSY;
+	}
+
+	SfAttLockFirmware(true);
+	if((ret = krtc_get_date_time(&tm)) < 0)
+	{
+		SfAttLockFirmware(false);
+		KALERT("%s: krtc_get_date_time failed!\n", __FUNCTION__);
+		return ret;
+	}
+	SfAttLockFirmware(false);
+
+	return sprintf(buf, "%04ld-%02d-%02dT%02d:%02d:%02d+00:00\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+static ssize_t uxts64bin_read(struct file *pf, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t len)
+{
+	int ret;
+	time64_t ts;
+	struct tm tm;
+	
+	if(off != 0)
+	{
+		KALERT("%s: Offset must be 0!\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	
+	if(len < sizeof(ts))
+	{
+		KALERT("%s: Insufficient buffer size: %zu! At least %zu bytes are required!\n", __FUNCTION__, len, sizeof(time64_t));
+		return -ENOMEM;
+	}
+
+	if(SfAttIsFirmwareLocked())
+	{
+		KALERT("%s: Firmware update in progress!\n", __FUNCTION__);
+		return -EBUSY;
+	}
+
+	SfAttLockFirmware(true);
+	if((ret = krtc_get_date_time(&tm)) < 0)
+	{
+		SfAttLockFirmware(false);
+		KALERT("%s: krtc_get_date_time failed!\n", __FUNCTION__);
+		return ret;
+	}
+	SfAttLockFirmware(false);
+
+	ts = mktime64(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+	memcpy(buf, &ts, sizeof(ts));
+	return sizeof(ts);
+}
+
+static ssize_t uxts64bin_write(struct file *pf, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t len)
+{
+	int ret;
+	time64_t ts;
+	struct tm tm;
+
+	if(off != 0 || len != sizeof(time64_t))
+	{
+		KALERT("%s: Offset must be 0 and length must be %zu byte!\n", __FUNCTION__, sizeof(time64_t));
+		return -EINVAL;
+	}
+
+	memcpy(&ts, buf, sizeof(ts));
+	
+	if((ts < KRTC_TIMESTAMP_2000_01_01) || (ts >= KRTC_TIMESTAMP_2100_01_01))
+	{
+		KALERT("%s: Date/Time must be >= 2000-01-01 00:00:00 and < 2100-01-01 00:00:00!\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	time64_to_tm(ts, 0, &tm);
+
+	if(SfAttIsFirmwareLocked())
+	{
+		KALERT("%s: Firmware update in progress!\n", __FUNCTION__);
+		return -EBUSY;
+	}
+
+	SfAttLockFirmware(true);
+	if((ret = krtc_set_date_time(&tm)) < 0)
+	{
+		SfAttLockFirmware(false);
+		KALERT("%s: krtc_get_date_time failed!\n", __FUNCTION__);
+		return ret;
+	}
+	SfAttLockFirmware(false);
+
+	return len;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t ctrl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "SysToRtc > 1\n" \
+						"RtcToSys > 2\n" \
+						"TestI2C  > 3\n");
+}
+
+static ssize_t ctrl_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	time64_t ts;
+	struct tm tm;
+	struct timespec64 tp;
+
+	if(SfAttIsFirmwareLocked())
+	{
+		KALERT("%s: Firmware update in progress!\n", __FUNCTION__);
+		return -EBUSY;
+	}
+
+	if(count == 2)
+	{
+		if(!isspace(buf[1]) && (buf[1] != 0))
+		{
+			KALERT("%s: Invalid input: \"%s\"!\n", __FUNCTION__, buf);
+			return -EINVAL;
+		}
+	}
+	else if(count != 1)
+	{
+		KALERT("%s: Invalid input: \"%s\"!\n", __FUNCTION__, buf);
+		return -EINVAL;
+	}
+
+	switch(*buf)
+	{
+	case 1:
+	case '1':
+//		KALERT("%s: Set System time to RTC\n", __FUNCTION__);
+		SfAttLockFirmware(true);
+		ts = ktime_get_real_seconds();
+		if((ts < KRTC_TIMESTAMP_2000_01_01) || (ts >= KRTC_TIMESTAMP_2100_01_01))
+		{
+			SfAttLockFirmware(false);
+			KALERT("%s: Date/Time must be >= 2000-01-01 00:00:00 and < 2100-01-01 00:00:00!\n", __FUNCTION__);
+			return -EINVAL;
+		}
+		time64_to_tm(ts, 0, &tm);
+		if((ret = krtc_set_date_time(&tm)) < 0)
+		{
+			SfAttLockFirmware(false);
+			KALERT("%s: krtc_get_date_time failed!\n", __FUNCTION__);
+			return ret;
+		}
+		SfAttLockFirmware(false);
+		break;
+	case 2:
+	case '2':
+//		KALERT("%s: Set RTC to System time\n", __FUNCTION__);
+		SfAttLockFirmware(true);
+		if((ret = krtc_get_date_time(&tm)) < 0)
+		{
+			SfAttLockFirmware(false);
+			KALERT("%s: krtc_get_date_time failed!\n", __FUNCTION__);
+			return ret;
+		}
+		ts = mktime64(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+		tp.tv_sec = ts;
+		tp.tv_nsec = 0;
+		do_settimeofday64(&tp);
+		SfAttLockFirmware(false);
+		break;
+	case 3:
+	case '3':
+		SfAttLockFirmware(true);
+		if((ret = krtc_test_i2c()))
+		{
+			SfAttLockFirmware(false);
+			KALERT("%s: I2C-Test error: %d!\n", __FUNCTION__, ret);
+			return ret;
+		}
+		else
+			KALERT("%s: I2C-Test success!\n", __FUNCTION__);
+		SfAttLockFirmware(false);
+		break;
+	default:
+		KALERT("%s: Invalid input: \"%c\"!\n", __FUNCTION__, *buf);
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	const char *pszType;
+	RTCTypes type = krtc_get_type();
+	
+	switch(type)
+	{
+	case RTCT_MCP7940:
+		pszType = "MCP7940";
+		break;
+	case RTCT_DS3231:
+		pszType = "DS3231";
+		break;
+	default:
+		pszType = "Unknown";
+		break;
+	}
+
+	return sprintf(buf, "%s\n", pszType);
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -284,38 +749,38 @@ void SfAttLockFirmware(bool bLock)
 
 /////////////////////////////////////////////////////////////////////////////
 
-unsigned int SfAttGetBacklightBrightness(void)
+unsigned int SfAttGetBacklightDutyCyclePercent(void)
 {
-	return (unsigned int)atomic_read(&g_valBacklightBrightness);
+	return (unsigned int)atomic_read(&g_valBacklightDutyCyclePercent);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-void SfAttSetBacklightBrightness(int val)
+void SfAttSetBacklightDutyCyclePercent(int val)
 {
-	if(val < 0)
-		val = 0;
-	else if(val > 100)
-		val = 100;
-	atomic_set(&g_valBacklightBrightness, val);
+	if(val < _BACKLIGHT_DUTY_CYCLE_PERC_MIN)
+		val = _BACKLIGHT_DUTY_CYCLE_PERC_MIN;
+	else if(val > _BACKLIGHT_DUTY_CYCLE_PERC_MAX)
+		val = _BACKLIGHT_DUTY_CYCLE_PERC_MAX;
+	atomic_set(&g_valBacklightDutyCyclePercent, val);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-unsigned int SfAttGetBacklightFrequency(void)
+unsigned int SfAttGetBacklightPeriod(void)
 {
-	return (unsigned int)atomic_read(&g_valBacklightFrequency);
+	return (unsigned int)atomic_read(&g_valBacklightPeriod);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-void SfAttSetBacklightFrequency(int val)
+void SfAttSetBacklightPeriod(int val)
 {
-	if(val < _BACKLIGHT_FREQ_MIN_HZ)
-		val = _BACKLIGHT_FREQ_MIN_HZ;
-	else if(val > _BACKLIGHT_FREQ_MAX_HZ)
-		val = _BACKLIGHT_FREQ_MAX_HZ;
-	atomic_set(&g_valBacklightFrequency, val);
+	if(val < _BACKLIGHT_PERIOD_MIN)
+		val = _BACKLIGHT_PERIOD_MIN;
+	else if(val > _BACKLIGHT_PERIOD_MAX)
+		val = _BACKLIGHT_PERIOD_MAX;
+	atomic_set(&g_valBacklightPeriod, val);
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -333,13 +798,14 @@ bool SfAttBacklightChanged(void)
 /////////////////////////////////////////////////////////////////////////////
 // tiva generic
 
+struct kobj_attribute g_tivaDevIdAtt	= __ATTR_RO(did);
 struct kobj_attribute g_tivaUptimeAtt	= __ATTR_RO(uptime);
 
 /////////////////////////////////////////////////////////////////////////////
 // backlight
 
-struct kobj_attribute g_tivaBrightnessAtt	= __ATTR_WO(brightness);
-struct kobj_attribute g_tivaFrequencyAtt	= __ATTR_WO(frequency);
+struct kobj_attribute g_tivaDutyCycleAtt	= __ATTR_WO(dutycycle);
+struct kobj_attribute g_tivaPeriodAtt		= __ATTR_WO(period);
 
 /////////////////////////////////////////////////////////////////////////////
 // ADC
@@ -356,4 +822,18 @@ struct bin_attribute g_tivaAdcBinAtt	= __BIN_ATTR_RO(AdcBin, sizeof(TIVA_ADC));
 // tiva firmware
 
 struct kobj_attribute g_tivaVersionAtt	= __ATTR_RO(version);
-struct bin_attribute g_tivaFwImageAtt	= __BIN_ATTR_RW(image, 0);
+struct bin_attribute g_tivaFwImageAtt	= __BIN_ATTR_RW(image, 0);
+
+/////////////////////////////////////////////////////////////////////////////
+// tiva memory
+
+struct bin_attribute g_tivaMemory		= __BIN_ATTR_RW(mem, 0);
+struct kobj_attribute g_tivaMemMap		= __ATTR_RO(map);
+
+/////////////////////////////////////////////////////////////////////////////
+// tiva RTC
+
+struct kobj_attribute g_tivaRtcIso8601	= __ATTR_RO(iso8601);
+struct bin_attribute g_tivaRtcUnTsBin64	= __BIN_ATTR_RW(uxts64bin, sizeof(time64_t));
+struct kobj_attribute g_tivaRtcCtrl		= __ATTR_RW(ctrl);
+struct kobj_attribute g_tivaRtcType		= __ATTR_RO(type);

+ 29 - 8
sfsattrib.h

@@ -6,6 +6,7 @@
 
 #include <linux/mutex.h>
 #include "defines.h"
+#include "krtc.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -17,9 +18,21 @@ extern "C" {
 extern int g_hw, g_sw;
 extern TIVA_ADC g_tadc;
 extern unsigned long long g_nUpTime;
+extern TIVA_DCAP g_dcaps;
+extern TIVA_MEMORY_MAP g_tiMM[];
 extern int g_backlight;
 
-extern struct kobj_attribute g_tivaVersionAtt;
+/*
+typedef enum
+{
+	MMI_Flash,
+	MMI_SRam,
+	MMI_Peripherals,
+	MMI_Count
+}MemoryMapIndex;*/
+
+extern struct kobj_attribute g_tivaDevIdAtt;
+extern struct kobj_attribute g_tivaUptimeAtt;
 
 extern struct kobj_attribute g_tivaUVersAtt;
 extern struct kobj_attribute g_tivaUBatV3Att;
@@ -29,12 +42,20 @@ extern struct kobj_attribute g_tivaUV3V6BatAtt;
 extern struct kobj_attribute g_tivaTempTIVAAtt;
 extern struct bin_attribute g_tivaAdcBinAtt;
 
-extern struct kobj_attribute g_tivaUptimeAtt;
-extern struct kobj_attribute g_tivaBrightnessAtt;
-extern struct kobj_attribute g_tivaFrequencyAtt;
+extern struct kobj_attribute g_tivaDutyCycleAtt;
+extern struct kobj_attribute g_tivaPeriodAtt;
 
+extern struct kobj_attribute g_tivaVersionAtt;
 extern struct bin_attribute g_tivaFwImageAtt;
 
+extern struct bin_attribute g_tivaMemory;
+extern struct kobj_attribute g_tivaMemMap;
+
+extern struct kobj_attribute g_tivaRtcIso8601;
+extern struct bin_attribute g_tivaRtcUnTsBin64;
+extern struct kobj_attribute g_tivaRtcCtrl;
+extern struct kobj_attribute g_tivaRtcType;
+
 bool SfAttInit(void);
 void SfAttExit(void);
 
@@ -42,10 +63,10 @@ void SfAttLockFirmware(bool bLock);
 bool SfAttIsFirmwareLocked(void);
 
 bool SfAttBacklightChanged(void);
-unsigned int SfAttGetBacklightBrightness(void);
-void SfAttSetBacklightBrightness(int val);
-unsigned int SfAttGetBacklightFrequency(void);
-void SfAttSetBacklightFrequency(int val);
+unsigned int SfAttGetBacklightDutyCyclePercent(void);
+void SfAttSetBacklightDutyCyclePercent(int val);
+unsigned int SfAttGetBacklightPeriod(void);
+void SfAttSetBacklightPeriod(int val);
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus

+ 0 - 248
test/main.c

@@ -1,248 +0,0 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/statvfs.h>
-#include <sys/statfs.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <poll.h>
-#include <fcntl.h>
-#include <errno.h>
-#include "../gfaspi.h"
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _USE_BIN_ADC						1
-
-/////////////////////////////////////////////////////////////////////////////
-
-#ifdef _DEBUG
-#define TRACE(...)							fprintf(stdout, __VA_ARGS__), fflush(stdout)
-#else	//	_DEBUG
-#define TRACE(...)
-#endif	//	_DEBUG
-
-#define UNUSED(v)							(void)v
-#define _countof(a)							(sizeof(a) / sizeof(*a))
-
-#define _CYCLE_INTV							500
-
-static bool g_bRun = true;
-
-/////////////////////////////////////////////////////////////////////////////
-
-typedef struct _ATTRIBS
-{
-	const char *pszName;
-	double (*fmt)(const char*);
-}ATTRIBS, *LPATTRIBS;
-typedef const ATTRIBS *LPCATTRIBS;
-
-/////////////////////////////////////////////////////////////////////////////
-
-static void _SigHandler(int sig)
-{
-	UNUSED(sig);
-	g_bRun = false;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-#if !_USE_BIN_ADC
-static double _FmtUVers(const char *pszVal)
-{
-	int nVal = atoi(pszVal);
-	return (double)nVal / 100.0 + 0.4;
-}
-
-static double _FmtUBatV3(const char *pszVal)
-{
-	int nVal = atoi(pszVal);
-	return (double)nVal / 100.0;
-}
-
-static double _FmtTemp(const char *pszVal)
-{
-	int nVal = atoi(pszVal);
-	return (double)nVal / 10.0;
-}
-
-static double _FmtUV5Vsys(const char *pszVal)
-{
-	int nVal = atoi(pszVal);
-	return (double)nVal / 100.0;
-}
-
-static double _FmtUV3V6Bat(const char *pszVal)
-{
-	int nVal = atoi(pszVal);
-	return (double)nVal / 100.0;
-}
-
-static double _FmtTempTIVA(const char *pszVal)
-{
-	int nVal = atoi(pszVal);
-	return 147.5 - 187.5 * (double)nVal / 4096.0;
-}
-#endif	//	_USE_BIN_ADC
-
-/////////////////////////////////////////////////////////////////////////////
-
-int main(int argc, char *argv[])
-{
-	static const ATTRIBS attribs[] =
-	{
-#if _USE_BIN_ADC
-		{
-			.pszName = "AdcBin"
-		}
-#else 
-{
-	//	_USE_BIN_ADC
-}
-		{
-			.pszName = "UVers",
-			.fmt = _FmtUVers
-		},
-		{
-			.pszName = "UBatV3",
-			.fmt = _FmtUBatV3
-		},
-		{
-            .pszName = "TempBoard",
-			.fmt = _FmtTemp
-		},
-		{
-			.pszName = "UV5Vsys",
-			.fmt = _FmtUV5Vsys
-		},
-		{
-			.pszName = "UV3V6Bat",
-			.fmt = _FmtUV3V6Bat
-		},
-		{
-			.pszName = "TempTIVA",
-			.fmt = _FmtTempTIVA
-		}
-#endif	//	_USE_BIN_ADC
-	};
-
-	const ATTRIBS *attribsCur[_countof(attribs)];
-
-	/////////////////////////////////////////////////////////////////////////
-
-#if _USE_BIN_ADC
-	TIVA_ADC tadc;
-#else	//	_USE_BIN_ADC
-	char szBuf[256];
-#endif	//	_USE_BIN_ADC
-	int nRet, nCntAtts, i;
-	struct pollfd pfd[6];
-	struct sigaction sa;
-
-	memset(pfd, 0, sizeof(pfd));
-	memset(&sa, 0, sizeof(sa));
-	UNUSED(argc);
-	UNUSED(argv);
-
-	/////////////////////////////////////////////////////////////////////////
-
-	// handle signals
-	sa.sa_handler = _SigHandler;
-    sigaction(SIGHUP, &sa, NULL);	// handles user's terminal disconnect
-    sigaction(SIGQUIT, &sa, NULL);	// handles Ctrl + '\'
-	sigaction(SIGTERM, &sa, NULL);	// handles normal termination
-	sigaction(SIGABRT, &sa, NULL);	// handles abnormal termination (i.e. abort())
-	sigaction(SIGINT, &sa, NULL);	// handles Ctrl + 'C'
-
-	// ignore signals
-	sa.sa_handler = SIG_IGN;
-    sigaction(SIGTSTP, &sa, NULL);	// ignores Ctrl + 'Z'
-    sigaction(SIGCHLD, &sa, NULL);	// ignores child process termination
-    sigaction(0, &sa, NULL);		// ignores shell termination
-
-	/////////////////////////////////////////////////////////////////////////
-
-	for(i = 0, nCntAtts = 0; i < (int)_countof(attribs); ++i)
-	{
-		int fd;
-		char szDevNode[128];
-        sprintf(szDevNode, "/sys/gfa/tiva/adc/%s", attribs[i].pszName);
-
-		if((fd = open(szDevNode, O_RDONLY, 0)) >= 0)
-		{
-			pfd[nCntAtts].fd = fd;
-			pfd[nCntAtts].events = POLLPRI;
-			attribsCur[nCntAtts] = &attribs[i];
-			++nCntAtts;
-		}
-	}
-
-	/////////////////////////////////////////////////////////////////////////
-	
-	if(nCntAtts > 0)
-	{
-		while(g_bRun && ((nRet = poll(pfd, nCntAtts, _CYCLE_INTV)) >= 0))
-		{
-			if(nRet > 0)
-			{
-#if !_USE_BIN_ADC
-				for(i = 0; i < nCntAtts; ++i)
-				{
-					if(pfd[i].revents & POLLPRI)
-					{
-						szBuf[0] = '\0';
-
-						if((nRet = read(pfd[i].fd, szBuf, sizeof(szBuf))) >= 0)
-						{
-							szBuf[nRet] = '\0';
-							lseek(pfd[i].fd, 0, SEEK_SET);
-							TRACE("%-8s: %s\n", attribsCur[i]->pszName, szBuf);
-						}
-						else
-						{
-							g_bRun = false;
-							break;
-						}
-					}
-				}
-#else	//	_USE_BIN_ADC
-				if(pfd[0].revents & POLLPRI)
-				{
-					if((nRet = read(pfd[0].fd, &tadc, sizeof(tadc))) >= 0)
-					{
-						lseek(pfd[0].fd, 0, SEEK_SET);
-						TRACE("UVers   : %.2f V\n",  (double)tadc.UVers / 100.0 + 0.4);
-						TRACE("UBatV3  : %.2f V\n",  (double)tadc.UBatV3 / 100.0);
-						TRACE("Temp    : %.2f °C\n", (double)tadc.Temp / 10.0);
-						TRACE("UV5Vsys : %.2f V\n",  (double)tadc.UV5Vsys / 100.0);
-						TRACE("UV3V6Bat: %.2f V\n",  (double)tadc.UV3V6Bat / 100.0);
-						TRACE("TempTIVA: %.2f °C\n\n", 147.5 - 187.5 * (double)tadc.TempTIVA / 4096.0);
-					}
-					else
-					{
-						g_bRun = false;
-						break;
-					}
-				}
-#endif	//	_USE_BIN_ADC
-			}
-		}
-
-		if(nRet < 0)
-		{
-			TRACE("%s\n", strerror(errno));
-		}
-	}
-
-	for(i = 0; i < nCntAtts; ++i)
-	{
-		if(pfd[i].fd >= 0)
-			close(pfd[i].fd);
-	}
-
-	return 0;
-}

+ 128 - 0
timeconv.c

@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: LGPL-2.0+
+/*
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+ * This file is part of the GNU C Library.
+ * Contributed by Paul Eggert (eggert@twinsun.com).
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The GNU C Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU C Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Converts the calendar time to broken-down time representation
+ * Based on code from glibc-2.6
+ *
+ * 2009-7-14:
+ *   Moved from glibc-2.6 to kernel by Zhaolei<zhaolei@cn.fujitsu.com>
+ */
+
+#include <linux/time.h>
+#include <linux/module.h>
+
+/*
+ * Nonzero if YEAR is a leap year (every 4 years,
+ * except every 100th isn't, and every 400th is).
+ */
+static int __isleap(long year)
+{
+	return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
+}
+
+/* do a mathdiv for long type */
+static long math_div(long a, long b)
+{
+	return a / b - (a % b < 0);
+}
+
+/* How many leap years between y1 and y2, y1 must less or equal to y2 */
+static long leaps_between(long y1, long y2)
+{
+	long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100)
+		+ math_div(y1 - 1, 400);
+	long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100)
+		+ math_div(y2 - 1, 400);
+	return leaps2 - leaps1;
+}
+
+/* How many days come before each month (0-12). */
+static const unsigned short __mon_yday[2][13] = {
+	/* Normal years. */
+	{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+	/* Leap years. */
+	{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
+};
+
+#define SECS_PER_HOUR	(60 * 60)
+#define SECS_PER_DAY	(SECS_PER_HOUR * 24)
+
+/**
+ * time64_to_tm - converts the calendar time to local broken-down time
+ *
+ * @totalsecs:	the number of seconds elapsed since 00:00:00 on January 1, 1970,
+ *		Coordinated Universal Time (UTC).
+ * @offset:	offset seconds adding to totalsecs.
+ * @result:	pointer to struct tm variable to receive broken-down time
+ */
+void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
+{
+	long days, rem, y;
+	int remainder;
+	const unsigned short *ip;
+
+	days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder);
+	rem = remainder;
+	rem += offset;
+	while (rem < 0) {
+		rem += SECS_PER_DAY;
+		--days;
+	}
+	while (rem >= SECS_PER_DAY) {
+		rem -= SECS_PER_DAY;
+		++days;
+	}
+
+	result->tm_hour = rem / SECS_PER_HOUR;
+	rem %= SECS_PER_HOUR;
+	result->tm_min = rem / 60;
+	result->tm_sec = rem % 60;
+
+	/* January 1, 1970 was a Thursday. */
+	result->tm_wday = (4 + days) % 7;
+	if (result->tm_wday < 0)
+		result->tm_wday += 7;
+
+	y = 1970;
+
+	while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
+		/* Guess a corrected year, assuming 365 days per year. */
+		long yg = y + math_div(days, 365);
+
+		/* Adjust DAYS and Y to match the guessed year. */
+		days -= (yg - y) * 365 + leaps_between(y, yg);
+		y = yg;
+	}
+
+	result->tm_year = y - 1900;
+
+	result->tm_yday = days;
+
+	ip = __mon_yday[__isleap(y)];
+	for (y = 11; days < ip[y]; y--)
+		continue;
+	days -= ip[y];
+
+	result->tm_mon = y;
+	result->tm_mday = days + 1;
+}