瀏覽代碼

Erster Commit.

Rind 5 年之前
父節點
當前提交
5a948a106f
共有 8 個文件被更改,包括 924 次插入228 次删除
  1. 1 1
      Makefile
  2. 208 67
      drvmain.c
  3. 345 160
      kspi.c
  4. 15 0
      kspi.h
  5. 81 0
      sfsattrib.c
  6. 31 0
      sfsattrib.h
  7. 212 0
      test/main.c
  8. 31 0
      test/spitest.pro

+ 1 - 1
Makefile

@@ -2,7 +2,7 @@
 TARGET_MODULE:=gfaspi
 PWD := $(shell pwd)
 
-$(TARGET_MODULE)-objs := drvmain.o kfile.o kspi.o
+$(TARGET_MODULE)-objs := drvmain.o kfile.o kspi.o sfsattrib.o
 obj-m := $(TARGET_MODULE).o
 
 

+ 208 - 67
drvmain.c

@@ -1,14 +1,20 @@
+#include <linux/version.h>
 #include <linux/init.h>
 #include <linux/kernel.h>    // printk()
+#include <linux/module.h>    // THIS_MODULE
 #include <linux/kobject.h>   // struct kobject
+#include <linux/timer.h>
 #include <linux/errno.h>     // error codes
-#include <linux/module.h>    // THIS_MODULE
 #include <linux/sysfs.h>
 #include <linux/syscalls.h>
+#include <linux/workqueue.h>
 #include <asm/ioctls.h>
 #include <linux/spi/spidev.h>
 #include "kfile.h"
 #include "kspi.h"
+#include "sfsattrib.h"
+
+#define _TIMER_INTERVAL				(jiffies + 3 * HZ)
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -18,54 +24,161 @@ MODULE_AUTHOR("GfA");
 /////////////////////////////////////////////////////////////////////////////
 
 #define _SPI_DEVICE				"/dev/spidev1.0"
-static struct file *g_pfSpiDev	= NULL;
+
+static void work_queue_func(struct work_struct *work);
+static struct kobject *g_pKoGfa = NULL, *g_pKoTiva = NULL;
 
 /////////////////////////////////////////////////////////////////////////////
+// timer
 
-static struct kobject *pKoGfa = NULL, *pKoTiva = NULL;
+static struct workqueue_struct *g_pwq = NULL;
+static DECLARE_WORK(g_wo, work_queue_func);
 
-/////////////////////////////////////////////////////////////////////////////
+static volatile bool g_bTimerRunning = false;
+static struct timer_list g_jiq_timer;
 
-static ssize_t firmware_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+static void OnJigTimer(unsigned long ptr)
 {
-	int hw = 0, sw = 0;
-	printk(KERN_ALERT "%s, TID: %d, \"%s\"\n", __FUNCTION__, current->pid, current->comm);
-	CmdGetFirmwareVersion(g_pfSpiDev, &hw, &sw);
-	return sprintf(buf, "HW: %08X SW: %08X\n", hw, sw);
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	queue_work(g_pwq, &g_wo);
 }
 
 /////////////////////////////////////////////////////////////////////////////
+// work queue
+// sysfs_notify
+// https://stackoverflow.com/questions/16367623/using-the-linux-sysfs-notify-call
+//
 
-static struct kobj_attribute tivaFirmwareAtt = __ATTR_RO(firmware);
-
-/////////////////////////////////////////////////////////////////////////////
-
-static int drv_init(void)
+static void work_queue_func(struct work_struct *work)
 {
-	long ret;
+	TIVA_ADC tadc;
+	struct file *pfSpiDev = NULL;
 	uint8_t mode = SPI_MODE_3;
 	uint8_t bits = 8;
 	uint32_t speed = 1000000;
+	unsigned long start = jiffies;
 
-	/////////////////////////////////////////////////////////////////////////
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+//	CmdGetFirmwareVersion(pfSpiDev, &g_hw, &g_sw);
+//	sysfs_notify(g_pKoTiva, NULL, "firmware");
 
-	do
+	if((pfSpiDev = kf_open(_SPI_DEVICE, O_RDWR, 0)))
 	{
-		if(!(g_pfSpiDev = kf_open(_SPI_DEVICE, O_RDWR, 0)))
+		if(kf_ioctl(pfSpiDev, SPI_IOC_WR_MODE, (unsigned long)&mode) < 0)
 		{
-			printk(KERN_ALERT "file_open failed\n");
-			break;
+			printk(KERN_ALERT "can't set spi mode\n");
+//			break;
+		}
+
+		if(kf_ioctl(pfSpiDev, SPI_IOC_RD_MODE, (unsigned long)&mode) < 0)
+		{
+			printk(KERN_ALERT "can't get spi mode\n");
+//			break;
 		}
 
 		/////////////////////////////////////////////////////////////////////
 
-		if(!(pKoGfa = kobject_create_and_add("gfa", /*kernel_kobj*/NULL)))
+		if(kf_ioctl(pfSpiDev, SPI_IOC_WR_BITS_PER_WORD, (unsigned long)&bits) < 0)
+		{
+			printk(KERN_ALERT "can't set bits per word\n");
+//			break;
+		}
+
+		if(kf_ioctl(pfSpiDev, SPI_IOC_RD_BITS_PER_WORD, (unsigned long)&bits) < 0)
+		{
+			printk(KERN_ALERT "can't get bits per word\n");
+//			break;
+		}
+
+		/////////////////////////////////////////////////////////////////////
+
+		if(kf_ioctl(pfSpiDev, SPI_IOC_WR_MAX_SPEED_HZ, (unsigned long)&speed) < 0)
+		{
+			printk(KERN_ALERT "can't set max speed hz\n");
+//			break;
+		}
+
+		if(kf_ioctl(pfSpiDev, SPI_IOC_RD_MAX_SPEED_HZ, (unsigned long)&speed) < 0)
+		{
+			printk(KERN_ALERT "can't get max speed hz\n");
+//			break;
+		}
+
+		if(CmdGetADC(pfSpiDev, &tadc) == 0)
+		{
+			if(g_tadc.UVers != tadc.UVers)
+			{
+				g_tadc.UVers = tadc.UVers;
+				sysfs_notify(g_pKoTiva, NULL, "UVers");
+//				printk(KERN_ALERT "Update UVers.\n");
+			}
+			
+			if(g_tadc.UBatV3 != tadc.UBatV3)
+			{
+				g_tadc.UBatV3 = tadc.UBatV3;
+				sysfs_notify(g_pKoTiva, NULL, "UBatV3");
+//				printk(KERN_ALERT "Update UBatV3.\n");
+			}
+			
+			if(g_tadc.Temp != tadc.Temp)
+			{
+				g_tadc.Temp = tadc.Temp;
+				sysfs_notify(g_pKoTiva, NULL, "Temp");
+//				printk(KERN_ALERT "Update Temp.\n");
+			}
+			
+			if(g_tadc.UV5Vsys != tadc.UV5Vsys)
+			{
+				g_tadc.UV5Vsys = tadc.UV5Vsys;
+				sysfs_notify(g_pKoTiva, NULL, "UV5Vsys");
+//				printk(KERN_ALERT "Update UV5Vsys.\n");
+			}
+			
+			if(g_tadc.UV3V6Bat != tadc.UV3V6Bat)
+			{
+				g_tadc.UV3V6Bat = tadc.UV3V6Bat;
+				sysfs_notify(g_pKoTiva, NULL, "UV3V6Bat");
+//				printk(KERN_ALERT "Update UV3V6Bat.\n");
+			}
+			
+			if(g_tadc.TempTIVA != tadc.TempTIVA)
+			{
+				g_tadc.TempTIVA = tadc.TempTIVA;
+				sysfs_notify(g_pKoTiva, NULL, "TempTIVA");
+//				printk(KERN_ALERT "Update TempTIVA.\n");
+			}
+		}
+
+		kf_close(pfSpiDev);
+	}
+	else
+	{
+		printk(KERN_ALERT "file_open failed\n");
+	}
+
+	if(g_bTimerRunning)
+	{
+		mod_timer(&g_jiq_timer, _TIMER_INTERVAL);
+	}
+	
+	printk(KERN_ALERT "Worker pass jiffies: %lu\n", ((long)jiffies - (long)start));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int drv_init(void)
+{
+	/////////////////////////////////////////////////////////////////////////
+
+	do
+	{
+		if(!(g_pKoGfa = kobject_create_and_add("gfa", NULL)))
 		{
 			printk(KERN_ALERT "kobject_create_and_add failed\n");
 			break;
 		}
 
-		if(!(pKoTiva = kobject_create_and_add("tiva", pKoGfa)))
+		if(!(g_pKoTiva = kobject_create_and_add("tiva", g_pKoGfa)))
 		{
 			printk(KERN_ALERT "kobject_create_and_add failed\n");
 			break;
@@ -73,89 +186,108 @@ static int drv_init(void)
 
 		/////////////////////////////////////////////////////////////////////
 
-		if(sysfs_create_file(pKoTiva, &tivaFirmwareAtt.attr))
+		if(sysfs_create_file(g_pKoTiva, &g_tivaFirmwareAtt.attr))
 		{
 			printk(KERN_ALERT "sysfs_create_file failed\n");
 			break;
 		}
 
-		/////////////////////////////////////////////////////////////////////
+		if(sysfs_create_file(g_pKoTiva, &g_tivaUVersAtt.attr))
+		{
+			printk(KERN_ALERT "sysfs_create_file failed\n");
+			break;
+		}
 
-		ret = kf_ioctl(g_pfSpiDev, SPI_IOC_WR_MODE, (unsigned long)&mode);
-		if(ret < 0)
+		if(sysfs_create_file(g_pKoTiva, &g_tivaUBatV3Att.attr))
 		{
-			printk(KERN_ALERT "can't set spi mode\n");
+			printk(KERN_ALERT "sysfs_create_file failed\n");
 			break;
 		}
 
-		mode = 0;
-		ret = kf_ioctl(g_pfSpiDev, SPI_IOC_RD_MODE, (unsigned long)&mode);
-		if(ret < 0)
+		if(sysfs_create_file(g_pKoTiva, &g_tivaTempAtt.attr))
 		{
-			printk(KERN_ALERT "can't get spi mode\n");
+			printk(KERN_ALERT "sysfs_create_file failed\n");
 			break;
 		}
 
-		/////////////////////////////////////////////////////////////////////
+		if(sysfs_create_file(g_pKoTiva, &g_tivaUV5VsysAtt.attr))
+		{
+			printk(KERN_ALERT "sysfs_create_file failed\n");
+			break;
+		}
 
-		ret = kf_ioctl(g_pfSpiDev, SPI_IOC_WR_BITS_PER_WORD, (unsigned long)&bits);
-		if(ret < 0)
+		if(sysfs_create_file(g_pKoTiva, &g_tivaUV3V6BatAtt.attr))
 		{
-			printk(KERN_ALERT "can't set bits per word\n");
+			printk(KERN_ALERT "sysfs_create_file failed\n");
 			break;
 		}
 
-		bits = 0;
-		ret = kf_ioctl(g_pfSpiDev, SPI_IOC_RD_BITS_PER_WORD, (unsigned long)&bits);
-		if(ret < 0)
+		if(sysfs_create_file(g_pKoTiva, &g_tivaTempTIVAAtt.attr))
 		{
-			printk(KERN_ALERT "can't get bits per word\n");
+			printk(KERN_ALERT "sysfs_create_file failed\n");
 			break;
 		}
 
 		/////////////////////////////////////////////////////////////////////
 
-		ret = kf_ioctl(g_pfSpiDev, SPI_IOC_WR_MAX_SPEED_HZ, (unsigned long)&speed);
-		if(ret < 0)
+		if(!(g_pwq = create_workqueue("GfaWrk")))
 		{
-			printk(KERN_ALERT "can't set max speed hz\n");
+			printk(KERN_ALERT "create_workqueue failed!\n");
 			break;
 		}
 
-		speed = 0;
-		ret = kf_ioctl(g_pfSpiDev, SPI_IOC_RD_MAX_SPEED_HZ, (unsigned long)&speed);
-		if(ret < 0)
+		/////////////////////////////////////////////////////////////////////
+
+		init_timer(&g_jiq_timer);
+		g_jiq_timer.function = OnJigTimer;
+		g_jiq_timer.data = 0;
+		g_jiq_timer.expires = _TIMER_INTERVAL;
+		add_timer(&g_jiq_timer);
+		g_bTimerRunning = true;
+
+		/////////////////////////////////////////////////////////////////////
+
+		if(!queue_work(g_pwq, &g_wo))
 		{
-			printk(KERN_ALERT "can't get max speed hz\n");
+			printk(KERN_ALERT "queue_work failed\n");
 			break;
 		}
 
 		/////////////////////////////////////////////////////////////////////
 
-		printk(KERN_ALERT "mode:  %hhu\n", mode);
-		printk(KERN_ALERT "bits:  %hhu\n", bits);
-		printk(KERN_ALERT "speed: %u\n", speed);
-		printk(KERN_ALERT "%s, TID: %d, \"%s\"\n", __FUNCTION__, current->pid, current->comm);
+		printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
 		return 0;
 	}
 	while(0);
 
 	/////////////////////////////////////////////////////////////////////////
+	
+	if(g_bTimerRunning)
+	{
+		g_bTimerRunning = false;
+		del_timer_sync(&g_jiq_timer);
+	}
 
-	if(g_pfSpiDev)
-		kf_close(g_pfSpiDev);
+	if(g_pwq)
+		destroy_workqueue(g_pwq);                           
 
-	if(pKoTiva)
+	if(g_pKoTiva)
 	{
-		sysfs_remove_file(pKoTiva, &tivaFirmwareAtt.attr);
-		kobject_put(pKoTiva);
-		pKoTiva = NULL;
+		sysfs_remove_file(g_pKoTiva, &g_tivaFirmwareAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUVersAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUBatV3Att.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaTempAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUV5VsysAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUV3V6BatAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaTempTIVAAtt.attr);
+		kobject_put(g_pKoTiva);
+		g_pKoTiva = NULL;
 	}
 
-	if(pKoGfa)
+	if(g_pKoGfa)
 	{
-		kobject_put(pKoGfa);
-		pKoGfa = NULL;
+		kobject_put(g_pKoGfa);
+		g_pKoGfa = NULL;
 	}
 
 	return -ENOMEM;
@@ -165,19 +297,28 @@ static int drv_init(void)
 
 static void drv_exit(void)
 {
-	if(g_pfSpiDev)
-		kf_close(g_pfSpiDev);
+	g_bTimerRunning = false;
+	del_timer_sync(&g_jiq_timer);
+
+	if(g_pwq)
+		destroy_workqueue(g_pwq);                           
 
-	if(pKoTiva)
+	if(g_pKoTiva)
 	{
-		sysfs_remove_file(pKoTiva, &tivaFirmwareAtt.attr);
-		kobject_put(pKoTiva);
+		sysfs_remove_file(g_pKoTiva, &g_tivaFirmwareAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUVersAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUBatV3Att.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaTempAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUV5VsysAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaUV3V6BatAtt.attr);
+		sysfs_remove_file(g_pKoTiva, &g_tivaTempTIVAAtt.attr);
+		kobject_put(g_pKoTiva);
 	}
 
-	if(pKoGfa)
-		kobject_put(pKoGfa);
+	if(g_pKoGfa)
+		kobject_put(g_pKoGfa);
 
-	printk(KERN_ALERT "%s, TID: %d, \"%s\"\n", __FUNCTION__, current->pid, current->comm);
+	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
 }
 
 /////////////////////////////////////////////////////////////////////////////

+ 345 - 160
kspi.c

@@ -1,160 +1,345 @@
-#include<linux/string.h>
-#include "kspi.h"
-#include "kfile.h"
-
-//uint8_t mode = SPI_MODE_3;
-//uint8_t bits = 8;
-//uint32_t speed = 1000000;
-#define CMD_ACK			0xcc
-#define CMD_NAK			0x33
-
-#define CMD_GET_FIRMWARE_VERSION 0x51
-#define CMD_GET_I2C 0x56
-#define CMD_SET_I2C 0x57
-#define CMD_GET_ADDRESS 0x58
-#define CMD_SET_ADDRESS 0x59
-#define CMD_SET_BACKLIGHT 0x5B
-#define CMD_GET_UPTIME 0x5C
-#define CMD_GET_ADC 0x5A
-
-static unsigned char transfer_1(struct file *pf, unsigned char Tx)
-{
-	int ret;
-	uint8_t tx = Tx;
-	uint8_t rx;
-
-	struct spi_ioc_transfer tr =
-	{
-		.tx_buf = (unsigned long) &tx,
-		.rx_buf = (unsigned long) &rx,
-		.len = 1,
-		.delay_usecs = 0,
-		.speed_hz = 1000000,
-		.bits_per_word = 8,
-	};
-
-	ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr);
-
-	if(ret < 0)
-	{
-		printk(KERN_ALERT "transfer_1 failed: %d\n", ret);
-	    return 0;
-	}
-
-    return rx;
-}
-
-static int WaitACK(struct file *pf)
-{
-    unsigned char rcv = 0;
-    int i = 0;
-//    time_t endwait = time(NULL) + 3; // max 3 seconds wait
-
-    // warten auf ACK
-
-    while ((rcv != CMD_ACK) && (rcv != CMD_NAK) && i < 100/*(time(NULL) < endwait)*/)
-    {
-        rcv = transfer_1(pf, 0);
-        ++i;
-    }
-
-    return rcv;
-}
-
-int SendCMD(struct file *pf, unsigned char Cmd, unsigned char DataLen, unsigned char *Data)
-{
-	unsigned char Len = DataLen + 3;
-	unsigned char Chk = Cmd;
-	int i;
-
-	printk(KERN_ALERT "%s, TID: %d, \"%s\"\n", __FUNCTION__, current->pid, current->comm);
-
-	for (i = 0; i < DataLen; i++)
-	    Chk += *(Data + i);
-
-	transfer_1(pf, Len);
-	transfer_1(pf, Chk);
-	transfer_1(pf, Cmd);
-
-	for (i = 0; i < DataLen; i++) {
-	    transfer_1(pf, *(Data + i));
-	}
-	return WaitACK(pf);
-}
-
-int ReadCmd(struct file *pf, int MaxLen, unsigned char *Data)
-{
-    unsigned char rcv = 0;
-    int i = 0;
-    int len;
-    unsigned char chksum = 0, chkval = 0;
-    int ret = 0;
-
-	printk(KERN_ALERT "%s, TID: %d, \"%s\"\n", __FUNCTION__, current->pid, current->comm);
-
-    // warten auf längenbyte der Antwort
-    while ((rcv == 0) && (i < 2000))
-    {
-        rcv = transfer_1(pf, 0);
-        ++i;
-    }
-
-    // daten einlesen
-    len = rcv;
-    for(i = 0; i < (len - 1); i++) {
-        rcv = transfer_1(pf, 0);
-        //printf("%2.2X-", rcv);
-        if(i == 0) { //CHKSUM
-            chkval = 0;
-            chksum = rcv;
-        } else {
-            chkval += rcv;
-            if((i - 1) < MaxLen)
-                *(Data + i - 1) = rcv;
-        }
-    }
-
-    if(chksum != chkval) {
-        ret = -1;
-    }
-    else {
-        ret = len - 2;
-    }
-
-    return ret;
-}
-
-int CmdGetFirmwareVersion(struct file *pf, int *Hw, int *Sw)
-{
-    int ret = -1;
-    int len = 0;
-
-	printk(KERN_ALERT "%s, TID: %d, \"%s\"\n", __FUNCTION__, current->pid, current->comm);
-
-    ret = SendCMD(pf, CMD_GET_FIRMWARE_VERSION , 0, NULL);
-    if(ret == CMD_ACK)
-    {
-        unsigned char Data[255];
-        memset(Data, 0, sizeof(Data));
-
-        if((len = ReadCmd(pf, sizeof(Data), Data)) == 9)
-        {
-            int HW_Version, SW_Version;
-
-            HW_Version = (Data[1] << 24) + (Data[2] << 16) + (Data[3] << 8) + Data[4];
-            SW_Version = (Data[5] << 24) + (Data[6] << 16) + (Data[7] << 8) + Data[8];
-
-            if(Hw != NULL) *Hw = HW_Version;
-            if(Sw != NULL) *Sw = SW_Version;
-            ret = 0;
-        } else {
-
-            printk(KERN_ALERT "LEN = %d\n", len);
-
-        }
-    }
-
-    transfer_1(pf, CMD_ACK); /* Testweise immer ACK */
-
-    return ret;
-}
+#include<linux/string.h>
+#include "kspi.h"
+#include "kfile.h"
+
+//uint8_t mode = SPI_MODE_3;
+//uint8_t bits = 8;
+//uint32_t speed = 1000000;
+#define CMD_ACK			0xcc
+#define CMD_NAK			0x33
+
+#define CMD_GET_FIRMWARE_VERSION 0x51
+#define CMD_GET_I2C 0x56
+#define CMD_SET_I2C 0x57
+#define CMD_GET_ADDRESS 0x58
+#define CMD_SET_ADDRESS 0x59
+#define CMD_SET_BACKLIGHT 0x5B
+#define CMD_GET_UPTIME 0x5C
+#define CMD_GET_ADC 0x5A
+
+static unsigned char transfer_1(struct file *pf, unsigned char Tx)
+{
+	int ret;
+	uint8_t tx = Tx;
+	uint8_t rx;
+
+	struct spi_ioc_transfer tr =
+	{
+		.tx_buf = (unsigned long) &tx,
+		.rx_buf = (unsigned long) &rx,
+		.len = 1,
+		.delay_usecs = 0,
+		.speed_hz = 1000000,
+		.bits_per_word = 8,
+	};
+
+	ret = kf_ioctl(pf, SPI_IOC_MESSAGE(1), (unsigned long)&tr);
+
+	if(ret < 0)
+	{
+		printk(KERN_ALERT "transfer_1 failed: %d\n", ret);
+	    return 0;
+	}
+
+    return rx;
+}
+
+static int WaitACK(struct file *pf)
+{
+    unsigned char rcv = 0;
+    int i = 0;
+
+    // warten auf ACK
+
+    while ((rcv != CMD_ACK) && (rcv != CMD_NAK) && i < 1000/*(time(NULL) < endwait)*/)
+    {
+        rcv = transfer_1(pf, 0);
+        ++i;
+    }
+
+    return rcv;
+}
+
+int SendCMD(struct file *pf, unsigned char Cmd, unsigned char DataLen, unsigned char *Data)
+{
+	unsigned char Len = DataLen + 3;
+	unsigned char Chk = Cmd;
+	int i;
+
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+
+	for (i = 0; i < DataLen; i++)
+	    Chk += *(Data + i);
+
+	transfer_1(pf, Len);
+	transfer_1(pf, Chk);
+	transfer_1(pf, Cmd);
+
+	for (i = 0; i < DataLen; i++) {
+	    transfer_1(pf, *(Data + i));
+	}
+	return WaitACK(pf);
+}
+
+int ReadCmd(struct file *pf, int MaxLen, unsigned char *Data)
+{
+    unsigned char rcv = 0;
+    int i = 0;
+    int len;
+    unsigned char chksum = 0, chkval = 0;
+    int ret = 0;
+
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+
+    // warten auf längenbyte der Antwort
+    while((rcv == 0) && (i < 2000))
+    {
+        rcv = transfer_1(pf, 0);
+        ++i;
+    }
+
+    // daten einlesen
+    len = rcv;
+    for(i = 0; i < (len - 1); ++i)
+    {
+        rcv = transfer_1(pf, 0);
+        //printf("%2.2X-", rcv);
+
+        if(i == 0) //CHKSUM
+        {
+            chkval = 0;
+            chksum = rcv;
+        }
+        else
+        {
+            chkval += rcv;
+            if((i - 1) < MaxLen)
+                *(Data + i - 1) = rcv;
+        }
+    }
+
+    if(chksum != chkval)
+    {
+        ret = -1;
+    }
+    else
+    {
+        ret = len - 2;
+    }
+
+    return ret;
+}
+
+int CmdGetFirmwareVersion(struct file *pf, int *Hw, int *Sw)
+{
+    int ret = -1;
+    int len = 0;
+
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+
+    ret = SendCMD(pf, CMD_GET_FIRMWARE_VERSION , 0, NULL);
+    if(ret == CMD_ACK)
+    {
+        unsigned char Data[255];
+        memset(Data, 0, sizeof(Data));
+
+        if((len = ReadCmd(pf, sizeof(Data), Data)) == 9)
+        {
+            int HW_Version, SW_Version;
+
+            HW_Version = (Data[1] << 24) + (Data[2] << 16) + (Data[3] << 8) + Data[4];
+            SW_Version = (Data[5] << 24) + (Data[6] << 16) + (Data[7] << 8) + Data[8];
+
+            if(Hw != NULL) *Hw = HW_Version;
+            if(Sw != NULL) *Sw = SW_Version;
+            ret = 0;
+        } else {
+            printk(KERN_ALERT "LEN = %d\n", len);
+        }
+    }
+
+    transfer_1(pf, CMD_ACK); /* Testweise immer ACK */
+
+    return ret;
+}
+
+unsigned long long CmdGetUptime(struct file *pf)
+{
+    unsigned long long ret = 0;
+    int len = 0;
+
+    ret = SendCMD(pf, CMD_GET_UPTIME , 0, NULL);
+    if(ret == CMD_ACK)
+    {
+        unsigned char Data[255];
+        ret = 0; //ACK erhalten soweit gut
+        memset(Data, 0, sizeof(Data));
+
+        if((len = ReadCmd(pf, sizeof(Data), Data)) == 9)
+        {
+            ret = (unsigned long long)(Data[1]) << 56 |
+                  (unsigned long long)(Data[2]) << 48 |
+                  (unsigned long long)(Data[3]) << 40 |
+                  (unsigned long long)(Data[4]) << 32 |
+                  (unsigned long long)(Data[5]) << 24 |
+                  (unsigned long long)(Data[6]) << 16 |
+                  (unsigned long long)(Data[7]) << 8 |
+                  (unsigned long long)(Data[8]);
+        }
+
+        transfer_1(pf, CMD_ACK); /**< ACK */
+    }
+    return ret;
+}
+
+int CmdGetAddress(struct file *pf, unsigned int Addr, unsigned int *Value)
+{
+    int ret = -1;
+    int len = 0;
+    unsigned char ReadADDRESS[4];
+    unsigned char DestData[10];
+    int DestLen = sizeof(DestData);
+
+    ReadADDRESS[0] = (unsigned char)((Addr >> 24) & 0xff);
+    ReadADDRESS[1] = (unsigned char)((Addr >> 16) & 0xff);
+    ReadADDRESS[2] = (unsigned char)((Addr >> 8) & 0xff);
+    ReadADDRESS[3] = (unsigned char)(Addr & 0xff);
+
+    ret = SendCMD(pf, CMD_GET_ADDRESS , sizeof(ReadADDRESS), ReadADDRESS);
+    if(ret == CMD_ACK) {
+        unsigned char Data[255];
+
+        ret = 0; //ACK erhalten soweit gut
+        memset(Data, 0, sizeof(Data));
+        if((len = ReadCmd(pf, sizeof(Data), Data)) > 0) {
+            memcpy(DestData, Data + 1, ((len - 1) <= DestLen)?(len - 1):DestLen);
+            ret = len - 1;
+        }
+        transfer_1(pf, CMD_ACK); /**< ACK */
+    }
+
+    if(Value)
+        *Value = (unsigned int)(DestData[0]) << 24 |
+                 (unsigned int)(DestData[1]) << 16 |
+                 (unsigned int)(DestData[2]) << 8 |
+                 (unsigned int)(DestData[3]);
+    return ret;
+}
+
+void getUPTIME(struct file *pf, unsigned int Addr, int nFwVersion)
+{
+    unsigned int dd;
+    int day, hour, min;
+
+    if(nFwVersion >= 0x113 )
+    {
+        unsigned long long Data;
+        Data = CmdGetUptime(pf);
+        day = Data / 86400000;
+        dd = Data - day * 86400000;
+        hour = dd / 3600000;
+        dd -= hour * 3600000;
+        min = dd / 60000;
+
+    }
+    else
+    {
+        unsigned int Data = 0xFFFFFFFF;
+
+        CmdGetAddress(pf, Addr, &Data);
+        day = Data / 86400000;
+        dd = Data - day * 86400000;
+        hour = dd / 3600000;
+        dd -= hour * 3600000;
+        min = dd / 60000;
+    }
+    
+    
+//    printf("%d d %2.2d:%2.2d\n", day, hour, min);
+}
+
+static const int g_kty_tab[][2] =
+{
+  {199, 1250},
+  {351, 1000},
+  {643, 750},
+  {1185, 500},
+  {2048, 250},
+  {3025,  0},
+  {3705, -250}
+};
+
+#define KTY_TAB_LEN				((sizeof g_kty_tab / sizeof g_kty_tab[0]) - 1)
+
+int lin_kty(int widerstand)
+{
+	int i;
+
+	if(widerstand <= g_kty_tab[0][0])
+		return(g_kty_tab[0][1]);
+
+	if(widerstand >= g_kty_tab[KTY_TAB_LEN][0])
+		return(g_kty_tab[KTY_TAB_LEN][1]);
+
+	/* Suchen des nächsten Tabelleneintrags */
+
+	for(i=1; i<=KTY_TAB_LEN; i++)
+	{
+		if(g_kty_tab[i][0] >= widerstand)
+		{
+			break;
+		}
+	}
+
+	/* Linear interpolieren */
+	return 	(long)g_kty_tab[i-1][1] +                            /* y1 */
+			(((long)g_kty_tab[i][1] - (long)g_kty_tab[i-1][1]) *   /* y2 - y1 */
+			((long)widerstand - (long)g_kty_tab[i-1][0]) /       /* x - x1 */
+			((long)g_kty_tab[i][0] - (long)g_kty_tab[i-1][0]));    /* x2 - x1 */
+}
+
+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);
+	abc = abc + out_min;
+	return abc;
+}
+
+int CmdGetADC(struct file *pf, LPTIVA_ADC padc)
+{
+    int ret;
+    int len = 0;
+
+    ret = SendCMD(pf, CMD_GET_ADC , 0, NULL);
+
+    if(ret == CMD_ACK)
+    {
+		unsigned short data[128];
+		memset(data, 0, sizeof(data));
+
+        if((len = ReadCmd(pf, sizeof(data) - 1, ((unsigned char*)data) + 1)) == 13)
+        {
+            padc->UVers		= scale(0, 4096, 0, 4375, ntohs(data[1]));	// val / 100.0 + 0.4
+            padc->UBatV3	= scale(0, 4096, 0, 500, ntohs(data[2]));	// val / 100.0
+            padc->Temp		= lin_kty(ntohs(data[3]));					// val / 10.0
+            padc->UV5Vsys	= scale(0, 4096, 0, 570, ntohs(data[4]));	// val / 100.0
+            padc->UV3V6Bat	= scale(0, 4096, 0, 500, ntohs(data[5]));	// val / 100.0
+            padc->TempTIVA	= ntohs(data[6]);							// 147.5 - 187.5 * val / 4096.0
+            ret = 0;
+        }
+        else
+        {
+            printk(KERN_ALERT "CmdGetADC - ReadCmd - LEN = %d\n", len);
+	    	ret = -1;
+        }
+    }
+    else
+    {
+		printk(KERN_ALERT "CmdGetADC - SendCMD - ret = %d\n", ret);
+    	ret = -1;
+    }
+
+	transfer_1(pf, CMD_ACK); /* Testweise immer ACK */
+    return ret;
+}

+ 15 - 0
kspi.h

@@ -15,9 +15,24 @@ extern "C" {
 /////////////////////////////////////////////////////////////////////////////
 // kspi.h - Declarations:
 
+typedef struct _TIVA_ADC
+{
+	int UVers;
+	int UBatV3;
+	int Temp;
+	int UV5Vsys;
+	int UV3V6Bat;
+	int TempTIVA;
+}TIVA_ADC, *LPTIVA_ADC;
+typedef const TIVA_ADC *LPCTIVA_ADC;
+
+/////////////////////////////////////////////////////////////////////////////
+
 int SendCMD(struct file *pf, unsigned char Cmd, unsigned char DataLen, unsigned char *Data);
 int ReadCmd(struct file *pf, int MaxLen, unsigned char *Data);
 int CmdGetFirmwareVersion(struct file *pf, int *Hw, int *Sw);
+void getUPTIME(struct file *pf, unsigned int Addr, int nFwVersion);
+int CmdGetADC(struct file *pf, LPTIVA_ADC padc);
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus

+ 81 - 0
sfsattrib.c

@@ -0,0 +1,81 @@
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/errno.h>
+#include <linux/sysfs.h>
+#include <linux/syscalls.h>
+#include "sfsattrib.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+int g_hw = 0, g_sw = 0;
+TIVA_ADC g_tadc;
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t firmware_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "HW: %08X SW: %08X", g_hw, g_sw);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t UVers_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "%d", g_tadc.UVers);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t UBatV3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "%d", g_tadc.UBatV3);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t Temp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "%d", g_tadc.Temp);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t UV5Vsys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "%d", g_tadc.UV5Vsys);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t UV3V6Bat_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "%d", g_tadc.UV3V6Bat);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ssize_t TempTIVA_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+//	printk(KERN_ALERT "%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid);
+	return sprintf(buf, "%d", g_tadc.TempTIVA);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+struct kobj_attribute g_tivaFirmwareAtt = __ATTR_RO(firmware);
+
+struct kobj_attribute g_tivaUVersAtt	= __ATTR_RO(UVers);
+struct kobj_attribute g_tivaUBatV3Att	= __ATTR_RO(UBatV3);
+struct kobj_attribute g_tivaTempAtt		= __ATTR_RO(Temp);
+struct kobj_attribute g_tivaUV5VsysAtt	= __ATTR_RO(UV5Vsys);
+struct kobj_attribute g_tivaUV3V6BatAtt	= __ATTR_RO(UV3V6Bat);
+struct kobj_attribute g_tivaTempTIVAAtt	= __ATTR_RO(TempTIVA);

+ 31 - 0
sfsattrib.h

@@ -0,0 +1,31 @@
+// sfsattrib.h :
+//
+
+#if !defined(AGD_SFSATTRIB_H__FA9B0DBB_F45F_475D_9C05_C528DA903997__INCLUDED_)
+#define AGD_SFSATTRIB_H__FA9B0DBB_F45F_475D_9C05_C528DA903997__INCLUDED_
+
+#include "kspi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif	//	__cplusplus
+
+/////////////////////////////////////////////////////////////////////////////
+// sfsattrib.h - Declarations:
+
+extern int g_hw, g_sw;
+extern TIVA_ADC g_tadc;
+extern struct kobj_attribute g_tivaFirmwareAtt;
+extern struct kobj_attribute g_tivaFirmwareAtt;
+extern struct kobj_attribute g_tivaUVersAtt;
+extern struct kobj_attribute g_tivaUBatV3Att;
+extern struct kobj_attribute g_tivaTempAtt;
+extern struct kobj_attribute g_tivaUV5VsysAtt;
+extern struct kobj_attribute g_tivaUV3V6BatAtt;
+extern struct kobj_attribute g_tivaTempTIVAAtt;
+
+/////////////////////////////////////////////////////////////////////////////
+#ifdef __cplusplus
+}
+#endif	//	__cplusplus
+#endif	//	!defined(AGD_SFSATTRIB_H__FA9B0DBB_F45F_475D_9C05_C528DA903997__INCLUDED_)

+ 212 - 0
test/main.c

@@ -0,0 +1,212 @@
+#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>
+
+/////////////////////////////////////////////////////////////////////////////
+
+#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;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[])
+{
+	static const ATTRIBS attribs[] =
+	{
+		{
+			.pszName = "UVers",
+			.fmt = _FmtUVers
+		},
+		{
+			.pszName = "UBatV3",
+			.fmt = _FmtUBatV3
+		},
+		{
+			.pszName = "Temp",
+			.fmt = _FmtTemp
+		},
+		{
+			.pszName = "UV5Vsys",
+			.fmt = _FmtUV5Vsys
+		},
+		{
+			.pszName = "UV3V6Bat",
+			.fmt = _FmtUV3V6Bat
+		},
+		{
+			.pszName = "TempTIVA",
+			.fmt = _FmtTempTIVA
+		}
+	};
+
+	const ATTRIBS *attribsCur[_countof(attribs)];
+
+	/////////////////////////////////////////////////////////////////////////
+
+	int nRet, nCntAtts, i;
+	char szBuf[256];
+	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
+
+/*	pfd[0].fd = open("/sys/gfa/tiva/UVers", O_RDONLY, 0);
+	pfd[1].fd = open("/sys/gfa/tiva/UBatV3", O_RDONLY, 0);
+	pfd[2].fd = open("/sys/gfa/tiva/Temp", O_RDONLY, 0);
+	pfd[3].fd = open("/sys/gfa/tiva/UV5Vsys", O_RDONLY, 0);
+	pfd[4].fd = open("/sys/gfa/tiva/UV3V6Bat", O_RDONLY, 0);
+	pfd[5].fd = open("/sys/gfa/tiva/TempTIVA", O_RDONLY, 0);*/
+
+	for(i = 0, nCntAtts = 0; i < (int)_countof(attribs); ++i)
+	{
+		int fd;
+		char szDevNode[128];
+		sprintf(szDevNode, "/sys/gfa/tiva/%s", attribs[i].pszName);
+
+		if((fd = open(szDevNode, O_RDONLY, 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)
+			{
+				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: %.2f\n", attribsCur[i]->pszName, attribsCur[i]->fmt(szBuf));
+						}
+						else
+						{
+							g_bRun = false;
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		if(nRet < 0)
+		{
+			TRACE("Error: %s\n", strerror(errno));
+		}
+	}
+
+	for(i = 0; i < nCntAtts; ++i)
+	{
+		if(pfd[i].fd >= 0)
+			close(pfd[i].fd);
+	}
+
+	return 0;
+}

+ 31 - 0
test/spitest.pro

@@ -0,0 +1,31 @@
+TEMPLATE = app
+CONFIG += console c++11 thread
+CONFIG -= qt app_bundle
+
+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
+}
+
+SOURCES += \
+    main.c
+
+HEADERS +=