ソースを参照

Add gsensor source code and test case.

Alfa 3 週間 前
コミット
f5cf925ca0
7 ファイル変更908 行追加2 行削除
  1. 38 0
      .gitignore
  2. 3 2
      Makefile
  3. 67 0
      Readme.md
  4. 503 0
      gsensor.c
  5. 241 0
      gsensor.h
  6. 11 0
      main.c
  7. 45 0
      test_app/test_gsensor.sh

+ 38 - 0
.gitignore

@@ -0,0 +1,38 @@
+# ---> C
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+*.mod
+*.d
+*.cmd
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+
+tags

+ 3 - 2
Makefile

@@ -3,7 +3,8 @@ CROSS_COMPILE=arm-poky-linux-gnueabi-
 #也可以同时编译多个模块  obj-m += export_symbol.o export_symbol1.o export_symbol2.o
 obj-m := coral.o
 
-coral-objs := main.o led.o light_ring.o ssegment.o ec_version.o buzzer.o fan.o writeprotect.o myname.o cash_drawers.o batteryled.o watchdog.o power.o switches.o backlight.o
+coral-objs := main.o led.o light_ring.o ssegment.o ec_version.o buzzer.o fan.o writeprotect.o myname.o cash_drawers.o batteryled.o watchdog.o power.o switches.o backlight.o gsensor.o
+
 
 KERNELDIR := /lib/modules/$(shell uname -r)/build
 ccflags-y += -I./include
@@ -14,4 +15,4 @@ all:
 	make -C $(KERNELDIR) M=$(PWD) modules
 clean:
 	rm -rf *.o
-	rm *.ko *.order *.mod.c *.symvers
+	rm *.ko *.order *.mod.c *.symvers

+ 67 - 0
Readme.md

@@ -0,0 +1,67 @@
+# C180_oyster_driver
+
+
+
+### 编译
+
+```
+$ make clean
+
+$ make
+```
+
+### 加载驱动
+
+```
+# 加载
+$ sudo insmod coral.ko
+# 检查加载是否成功
+$ lsmod | grep coral
+
+# 卸载
+$ sudo rmmod coral
+
+```
+
+**1. Test for gsensor:**
+
+```
+# 检查 Verifone 接口
+$ ls -la /sys/kernel/vfiec/gsensor/
+
+# 检查是否启用
+$ cat /sys/kernel/vfiec/gsensor/enable
+# 应输出: 1
+
+# 启用传感器
+$ echo 1 | sudo tee /sys/kernel/vfiec/gsensor/enable
+
+# 读取屏幕方向(字符串)
+$ cat /sys/kernel/vfiec/gsensor/screen_orientation
+# 应输出: portrait, landscape, portrait_flip, 或 landscape_flip
+
+# 读取屏幕方向(十六进制)
+$ cat /sys/kernel/vfiec/gsensor/instantaneous_orientation
+# 应输出: 0x14, 0x15, 0x16, 或 0x17
+
+# 查看原始数据
+$ cat /sys/kernel/vfiec/gsensor/raw_data
+# 应输出: X Y Z 值(单位: mg)
+
+# 查看当前模式
+$ cat /sys/kernel/vfiec/gsensor/mode
+# 输出示例: 0x03 (BIT0=1:中断模式, BIT1=1:已初始化)
+
+# 切换到轮询模式
+$ echo 0x01 | sudo tee /sys/kernel/vfiec/gsensor/mode
+
+# 查看状态
+$ cat /sys/kernel/vfiec/gsensor/state
+# BIT0: 是否启用, BIT1: 中断是否启用
+
+# Test with sh script:
+$ sudo test_app/test_gsensor.sh
+```
+
+
+

+ 503 - 0
gsensor.c

@@ -0,0 +1,503 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Verifone Gsensor driver for screen orientation
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+extern struct kobject *vfiec_kobj;
+
+/* Verifone API Definitions */
+enum gsensor_orientation_hex {
+    GSENSOR_ORIENT_UNKNOWN_HEX      = 0x00,
+    GSENSOR_ORIENT_PORTRAIT_HEX     = 0x14,
+    GSENSOR_ORIENT_LANDSCAPE_HEX    = 0x15,
+    GSENSOR_ORIENT_PORTRAIT_FLIP_HEX = 0x16,
+    GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX = 0x17,
+};
+
+#define GSENSOR_ORIENT_PORTRAIT_STR     "portrait"
+#define GSENSOR_ORIENT_LANDSCAPE_STR    "landscape"
+#define GSENSOR_ORIENT_PORTRAIT_FLIP_STR "portrait_flip"
+#define GSENSOR_ORIENT_LANDSCAPE_FLIP_STR "landscape_flip"
+
+#define GSENSOR_MODE_SOFT_RESET     BIT(2)
+#define GSENSOR_MODE_INITIALIZED    BIT(1)
+
+/* Register Definitions */
+#define ST_ACCEL_WHO_AM_I_ADDR      0x0F
+#define ST_ACCEL_CTRL_REG1_ADDR     0x20
+#define ST_ACCEL_CTRL_REG4_ADDR     0x23
+#define ST_ACCEL_OUT_X_L_ADDR       0x28
+
+#define SC7A20_WHO_AM_I_VALUE       0x11
+
+/* Configuration */
+#define GSENSOR_DEBOUNCE_MS     300
+#define GSENSOR_POLL_INTERVAL_MS 200
+
+struct gsensor_data {
+    struct i2c_client *client;
+    struct mutex lock;
+    struct delayed_work poll_work;
+    struct kobject *gsensor_kobj;
+    
+    bool enabled;
+    bool initialized;
+    
+    enum gsensor_orientation_hex orientation_hex;
+    enum gsensor_orientation_hex pending_orientation;
+    char orientation_str[32];
+    unsigned long last_change_jiffies;
+    
+    u8 who_am_i;
+};
+
+static struct gsensor_data *g_data = NULL;
+
+/* I2C Helper Functions */
+static int gsensor_read_reg(struct i2c_client *client, u8 reg, u8 *data)
+{
+    int ret = i2c_smbus_read_byte_data(client, reg);
+    if (ret < 0)
+        return ret;
+    *data = ret;
+    return 0;
+}
+
+static int gsensor_write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+    return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Read the raw data */
+static int gsensor_read_data(struct gsensor_data *data, int *x, int *y, int *z)
+{
+    u8 xl, xh, yl, yh, zl, zh;
+    s16 raw_x, raw_y, raw_z;
+    int ret;
+
+    /* Read the X-axis byte by byte */
+    ret = gsensor_read_reg(data->client, 0x28, &xl);
+    if (ret < 0) return ret;
+    ret = gsensor_read_reg(data->client, 0x29, &xh);
+    if (ret < 0) return ret;
+
+    /* Read the Y-axis byte by byte */
+    ret = gsensor_read_reg(data->client, 0x2a, &yl);
+    if (ret < 0) return ret;
+    ret = gsensor_read_reg(data->client, 0x2b, &yh);
+    if (ret < 0) return ret;
+
+    /* Z-axis */
+    ret = gsensor_read_reg(data->client, 0x2c, &zl);
+    if (ret < 0) return ret;
+    ret = gsensor_read_reg(data->client, 0x2d, &zh);
+    if (ret < 0) return ret;
+    
+    raw_x = (s16)((xh << 8) | xl);
+    raw_y = (s16)((yh << 8) | yl);
+    raw_z = (s16)((zh << 8) | zl);
+
+    /* ±2g Mode: 1 LSB = 0.06103515625 mg */
+    *x = (raw_x * 61) / 1000;
+    *y = (raw_y * 61) / 1000;
+    *z = (raw_z * 61) / 1000;
+    
+    return 0;
+}
+
+/* Enable/Disable sensor */
+static int gsensor_set_enable(struct gsensor_data *data, bool enable)
+{
+    int ret;
+    
+    if (enable) {
+        /* CTRL_REG1: 0x87 = 100Hz (0x80) + Enable all axes (0x07) */
+        ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x87);
+        if (ret < 0)
+            return ret;
+        msleep(20);
+    } else {
+        /* CTRL_REG1: 0x00 = Standby mode */
+        ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
+        if (ret < 0)
+            return ret;
+    }
+    
+    return 0;
+}
+
+/* Initialize sensor */
+static int gsensor_init(struct gsensor_data *data)
+{
+    u8 who_am_i;
+    int ret;
+
+    /* 1. Verify device */
+    ret = gsensor_read_reg(data->client, ST_ACCEL_WHO_AM_I_ADDR, &who_am_i);
+    if (ret < 0) {
+        dev_err(&data->client->dev, "Failed to read WHO_AM_I: %d\n", ret);
+        return ret;
+    }
+    
+    data->who_am_i = who_am_i;
+    dev_info(&data->client->dev, "WHO_AM_I = 0x%02x\n", who_am_i);
+    
+    if (who_am_i != SC7A20_WHO_AM_I_VALUE) {
+        dev_err(&data->client->dev, "Invalid WHO_AM_I: 0x%02x\n", who_am_i);
+        return -ENODEV;
+    }
+
+    /* 2. Setting CTRL_REG4: BDU enable , ±2g */
+    ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG4_ADDR, 0x80);
+    if (ret < 0)
+        return ret;
+
+    /* 3. Disable the sensor at startup */
+    ret = gsensor_write_reg(data->client, ST_ACCEL_CTRL_REG1_ADDR, 0x00);
+    if (ret < 0)
+        return ret;
+    
+    data->initialized = true;
+    dev_info(&data->client->dev, "Gsensor initialized successfully\n");
+    
+    return 0;
+}
+
+/* Orientation Calculation */
+static enum gsensor_orientation_hex gsensor_calc_orientation(int x, int y, int z)
+{
+    int abs_x = abs(x);
+    int abs_y = abs(y);
+    int abs_z = abs(z);
+
+    /* Device flat - Z axis maximum */
+    if (abs_z > 800 && abs_z > abs_x && abs_z > abs_y) {
+        return GSENSOR_ORIENT_UNKNOWN_HEX;
+    }
+
+    /* Landscape mode: X axis maximum */
+    if (abs_x > 800 && abs_x > abs_y) {
+        if (x > 0)
+            return GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX;
+        else
+            return GSENSOR_ORIENT_LANDSCAPE_HEX;
+    }
+
+    /* Portrait mode: Y axis maximum. */
+    if (abs_y > 800 && abs_y > abs_x) {
+        if (y > 0)
+            return GSENSOR_ORIENT_PORTRAIT_FLIP_HEX;
+        else
+            return GSENSOR_ORIENT_PORTRAIT_HEX;
+    }
+    
+    return GSENSOR_ORIENT_UNKNOWN_HEX;
+}
+
+static void gsensor_update_orientation(struct gsensor_data *data)
+{
+    int x, y, z;
+    enum gsensor_orientation_hex new_orient;
+    unsigned long now = jiffies;
+    
+    if (!data->enabled)
+        return;
+    
+    if (gsensor_read_data(data, &x, &y, &z) < 0)
+        return;
+    
+    new_orient = gsensor_calc_orientation(x, y, z);
+    
+    if (new_orient != GSENSOR_ORIENT_UNKNOWN_HEX && new_orient != data->orientation_hex) {
+        if (new_orient == data->pending_orientation) {
+            if (time_after(now, data->last_change_jiffies + 
+                           msecs_to_jiffies(GSENSOR_DEBOUNCE_MS))) {
+                data->orientation_hex = new_orient;
+                data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
+                
+                switch (new_orient) {
+                case GSENSOR_ORIENT_PORTRAIT_HEX:
+                    strcpy(data->orientation_str, GSENSOR_ORIENT_PORTRAIT_STR);
+                    break;
+                case GSENSOR_ORIENT_LANDSCAPE_HEX:
+                    strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
+                    break;
+                case GSENSOR_ORIENT_PORTRAIT_FLIP_HEX:
+                    strcpy(data->orientation_str, GSENSOR_ORIENT_PORTRAIT_FLIP_STR);
+                    break;
+                case GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX:
+                    strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_FLIP_STR);
+                    break;
+                default:
+                    break;
+                }
+                
+                dev_info(&data->client->dev, "Orientation: %s [X=%d, Y=%d, Z=%d]\n",
+                         data->orientation_str, x, y, z);
+            }
+        } else {
+            data->pending_orientation = new_orient;
+            data->last_change_jiffies = now;
+        }
+    } else if (new_orient == data->orientation_hex) {
+        data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
+    }
+}
+
+static void gsensor_poll_work(struct work_struct *work)
+{
+    struct gsensor_data *data = container_of(work, struct gsensor_data,
+                                             poll_work.work);
+    
+    mutex_lock(&data->lock);
+    gsensor_update_orientation(data);
+    mutex_unlock(&data->lock);
+    
+    if (data->enabled)
+        schedule_delayed_work(&data->poll_work, 
+                              msecs_to_jiffies(GSENSOR_POLL_INTERVAL_MS));
+}
+
+/* Sysfs Interface */
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+    return sprintf(buf, "%d\n", g_data ? g_data->enabled : 0);
+}
+
+static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr,
+                            const char *buf, size_t count)
+{
+    struct gsensor_data *data = g_data;
+    unsigned long val;
+    int ret;
+    
+    if (!data)
+        return -ENODEV;
+    
+    ret = kstrtoul(buf, 0, &val);
+    if (ret)
+        return ret;
+    
+    mutex_lock(&data->lock);
+    
+    if (val && !data->enabled) {
+        ret = gsensor_set_enable(data, true);
+        if (ret == 0) {
+            data->enabled = true;
+            schedule_delayed_work(&data->poll_work, 0);
+            gsensor_update_orientation(data);
+        }
+    } else if (!val && data->enabled) {
+        cancel_delayed_work_sync(&data->poll_work);
+        ret = gsensor_set_enable(data, false);
+        if (ret == 0)
+            data->enabled = false;
+    }
+    
+    mutex_unlock(&data->lock);
+    return ret < 0 ? ret : count;
+}
+static struct kobj_attribute enable_attr = __ATTR_RW(enable);
+
+static ssize_t screen_orientation_show(struct kobject *kobj,
+                                       struct kobj_attribute *attr, char *buf)
+{
+    return sprintf(buf, "%s\n", g_data ? g_data->orientation_str : "unknown");
+}
+static struct kobj_attribute screen_orientation_attr = __ATTR_RO(screen_orientation);
+
+static ssize_t instantaneous_orientation_show(struct kobject *kobj,
+                                              struct kobj_attribute *attr, char *buf)
+{
+    return sprintf(buf, "0x%02x\n", g_data ? g_data->orientation_hex : 0);
+}
+static struct kobj_attribute instantaneous_orientation_attr = __ATTR_RO(instantaneous_orientation);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+    u8 mode = 0;
+    if (g_data && g_data->initialized)
+        mode |= GSENSOR_MODE_INITIALIZED;
+    return sprintf(buf, "0x%02x\n", mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
+                          const char *buf, size_t count)
+{
+    struct gsensor_data *data = g_data;
+    unsigned long val;
+    
+    if (!data)
+        return -ENODEV;
+    
+    if (kstrtoul(buf, 0, &val))
+        return -EINVAL;
+    
+    if (val & GSENSOR_MODE_SOFT_RESET) {
+        mutex_lock(&data->lock);
+        gsensor_init(data);
+        data->orientation_hex = GSENSOR_ORIENT_LANDSCAPE_HEX;
+        strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
+        mutex_unlock(&data->lock);
+    }
+    
+    return count;
+}
+static struct kobj_attribute mode_attr = __ATTR_RW(mode);
+
+static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+    u8 state = 0;
+    if (g_data && g_data->enabled)
+        state |= 0x01;
+    return sprintf(buf, "0x%02x\n", state);
+}
+static struct kobj_attribute state_attr = __ATTR_RO(state);
+
+static ssize_t raw_data_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+    int x, y, z;
+    
+    if (!g_data)
+        return -ENODEV;
+    
+    if (gsensor_read_data(g_data, &x, &y, &z) < 0)
+        return -EIO;
+    
+    return sprintf(buf, "%d %d %d\n", x, y, z);
+}
+static struct kobj_attribute raw_data_attr = __ATTR_RO(raw_data);
+
+static struct attribute *gsensor_attrs[] = {
+    &enable_attr.attr,
+    &screen_orientation_attr.attr,
+    &instantaneous_orientation_attr.attr,
+    &mode_attr.attr,
+    &state_attr.attr,
+    &raw_data_attr.attr,
+    NULL,
+};
+
+static const struct attribute_group gsensor_attr_group = {
+    .attrs = gsensor_attrs,
+};
+
+/* I2C Probe/Remove */
+static int gsensor_probe(struct i2c_client *client)
+{
+    struct gsensor_data *data;
+    int ret;
+    
+    dev_info(&client->dev, "Gsensor probe\n");
+    
+    data = kzalloc(sizeof(*data), GFP_KERNEL);
+    if (!data)
+        return -ENOMEM;
+    
+    data->client = client;
+    mutex_init(&data->lock);
+    INIT_DELAYED_WORK(&data->poll_work, gsensor_poll_work);
+    data->orientation_hex = GSENSOR_ORIENT_LANDSCAPE_HEX;
+    strcpy(data->orientation_str, GSENSOR_ORIENT_LANDSCAPE_STR);
+    data->pending_orientation = GSENSOR_ORIENT_UNKNOWN_HEX;
+    data->last_change_jiffies = jiffies;
+    
+    i2c_set_clientdata(client, data);
+    
+    ret = gsensor_init(data);
+    if (ret < 0) {
+        dev_err(&client->dev, "Init failed: %d\n", ret);
+        goto err_free;
+    }
+    
+    g_data = data;
+    
+    if (vfiec_kobj) {
+        data->gsensor_kobj = kobject_create_and_add("gsensor", vfiec_kobj);
+        if (data->gsensor_kobj) {
+            ret = sysfs_create_group(data->gsensor_kobj, &gsensor_attr_group);
+            if (ret < 0) {
+                dev_err(&client->dev, "Failed to create sysfs group\n");
+                kobject_put(data->gsensor_kobj);
+                goto err_free;
+            }
+        } else {
+            dev_err(&client->dev, "Failed to create gsensor kobject\n");
+            goto err_free;
+        }
+    } else {
+        dev_err(&client->dev, "vfiec_kobj not available\n");
+        goto err_free;
+    }
+    
+    dev_info(&client->dev, "Gsensor driver loaded\n");
+    return 0;
+
+err_free:
+    kfree(data);
+    g_data = NULL;
+    return ret;
+}
+
+static int gsensor_remove(struct i2c_client *client)
+{
+    struct gsensor_data *data = i2c_get_clientdata(client);
+    
+    if (data) {
+        cancel_delayed_work_sync(&data->poll_work);
+        if (data->enabled)
+            gsensor_set_enable(data, false);
+        if (data->gsensor_kobj) {
+            sysfs_remove_group(data->gsensor_kobj, &gsensor_attr_group);
+            kobject_put(data->gsensor_kobj);
+        }
+        mutex_destroy(&data->lock);
+        kfree(data);
+        g_data = NULL;
+    }
+    return 0;
+}
+
+/* I2C Driver */
+static const struct i2c_device_id gsensor_id_table[] = {
+    { "sc7a20", 0 },
+    { "gsensor", 0 },
+    {}
+};
+MODULE_DEVICE_TABLE(i2c, gsensor_id_table);
+
+static struct i2c_driver gsensor_driver = {
+    .driver = { .name = "verifone-gsensor" },
+    .probe_new = gsensor_probe,
+    .remove = gsensor_remove,
+    .id_table = gsensor_id_table,
+};
+
+/* Init/Exit */
+int gsensor_init_main(void)
+{
+    printk(KERN_INFO "gsensor: Registering driver\n");
+    return i2c_add_driver(&gsensor_driver);
+}
+EXPORT_SYMBOL(gsensor_init_main);
+
+void gsensor_exit_main(void)
+{
+    printk(KERN_INFO "gsensor: Unregistering driver\n");
+    i2c_del_driver(&gsensor_driver);
+}
+EXPORT_SYMBOL(gsensor_exit_main);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Verifone, Inc.");
+MODULE_DESCRIPTION("Verifone Gsensor driver for screen orientation");

+ 241 - 0
gsensor.h

@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Memsic MXC400xXC Gsensor driver for screen orientation
+ * 
+ */
+
+#ifndef __GSENSOR_H
+#define __GSENSOR_H
+
+#include <linux/types.h>
+#include <linux/iio/iio.h>
+#include <linux/i2c.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Gsensor orientation values (hex per API spec) */
+enum gsensor_orientation_hex
+{
+    GSENSOR_ORIENT_UNKNOWN_HEX        = 0x00,
+    GSENSOR_ORIENT_PORTRAIT_HEX       = 0x14,
+    GSENSOR_ORIENT_LANDSCAPE_HEX      = 0x15,
+    GSENSOR_ORIENT_PORTRAIT_FLIP_HEX  = 0x16,
+    GSENSOR_ORIENT_LANDSCAPE_FLIP_HEX = 0x17,
+};
+
+/* Gsensor orientation strings */
+#define GSENSOR_ORIENT_PORTRAIT_STR       "portrait"
+#define GSENSOR_ORIENT_LANDSCAPE_STR      "landscape"
+#define GSENSOR_ORIENT_PORTRAIT_FLIP_STR  "portrait_flip"
+#define GSENSOR_ORIENT_LANDSCAPE_FLIP_STR "landscape_flip"
+
+/* Gsensor mode bits (per API spec) */
+#define GSENSOR_MODE_SOFT_RESET           BIT(2) /* EC17: soft reset */
+#define GSENSOR_MODE_INITIALIZED          BIT(1) /* EC16: 0=not init, 1=init */
+#define GSENSOR_MODE_INTERRUPT            BIT(0) /* 0=poll, 1=interrupt */
+
+/* Memsic MXC400xXC Register Definitions */
+#define MXC400X_REG_WHO_AM_I              0x00
+#define MXC400X_WHO_AM_I_VALUE            0x20
+
+#define MXC400X_REG_XOUT_U                0x04
+#define MXC400X_REG_XOUT_L                0x05
+#define MXC400X_REG_YOUT_U                0x06
+#define MXC400X_REG_YOUT_L                0x07
+#define MXC400X_REG_ZOUT_U                0x08
+#define MXC400X_REG_ZOUT_L                0x09
+
+#define MXC400X_REG_CONTROL               0x0A
+#define MXC400X_CONTROL_MODE_MASK         0x03
+#define MXC400X_CONTROL_MODE_STANDBY      0x00
+#define MXC400X_CONTROL_MODE_ACTIVE       0x01
+#define MXC400X_CONTROL_MODE_RESET        0x02
+
+#define MXC400X_REG_INT_ENABLE            0x0B
+#define MXC400X_INT_ENABLE_DRDY           BIT(0)
+
+#define MXC400X_REG_INT_STATUS            0x0C
+
+
+/* ST LIS2DH12 / SC7A20 register */
+#define ST_ACCEL_WHO_AM_I_ADDR         0x0F
+#define ST_ACCEL_WHO_AM_I_VALUE        0x33  /* LIS2DH12 / SC7A20 */
+#define ST_ACCEL_CTRL_REG1_ADDR        0x20
+#define ST_ACCEL_CTRL_REG4_ADDR        0x23
+#define ST_ACCEL_OUT_X_L_ADDR          0x28
+#define ST_ACCEL_OUT_X_H_ADDR          0x29
+#define ST_ACCEL_OUT_Y_L_ADDR          0x2A
+#define ST_ACCEL_OUT_Y_H_ADDR          0x2B
+#define ST_ACCEL_OUT_Z_L_ADDR          0x2C
+#define ST_ACCEL_OUT_Z_H_ADDR          0x2D
+#define ST_ACCEL_STATUS_REG_ADDR       0x27
+#define ST_ACCEL_STATUS_DRDY           0x07
+
+/* Driver Constants */
+#define GSENSOR_NUMBER_DATA_CHANNELS      3
+#define GSENSOR_DEBOUNCE_MS               100
+#define MXC400X_SCALE_FACTOR              1000 /* mg per count at 2g range */
+
+/* Default I2C address */
+#define MXC400X_DEFAULT_I2C_ADDR          0x15
+
+/* Sensor Settings Structure */
+struct gsensor_fs_avl
+{
+    unsigned int num;   /* Full scale value in g */
+    unsigned int value; /* Register value */
+    unsigned int gain;  /* Gain in micro g per LSB */
+};
+
+struct gsensor_odr_avl
+{
+    unsigned int hz;    /* Output data rate in Hz */
+    unsigned int value; /* Register value */
+};
+
+struct gsensor_settings
+{
+    u8 wai;                         /* WHO_AM_I expected value */
+    u8 wai_addr;                    /* WHO_AM_I register address */
+    const char **sensors_supported; /* Supported device names */
+
+    /* Channel configuration */
+    const struct iio_chan_spec *ch;
+    int num_channels;
+
+    /* ODR configuration */
+    struct
+    {
+        u8 addr;
+        u8 mask;
+        const struct gsensor_odr_avl *odr_avl;
+        int num_odr;
+    } odr;
+
+    /* Power mode configuration */
+    struct
+    {
+        u8 addr;
+        u8 mask;
+        u8 value_on;
+        u8 value_off;
+    } pw;
+
+    /* Full scale configuration */
+    struct
+    {
+        u8 addr;
+        u8 mask;
+        const struct gsensor_fs_avl *fs_avl;
+        int num_fs;
+    } fs;
+
+    /* Block Data Update */
+    struct
+    {
+        u8 addr;
+        u8 mask;
+    } bdu;
+
+    /* Data ready interrupt */
+    struct
+    {
+        struct
+        {
+            u8 addr;
+            u8 mask;
+        } int1;
+        u8 addr_ihl;
+        u8 mask_ihl;
+        struct
+        {
+            u8 addr;
+            u8 mask;
+        } stat_drdy;
+    } drdy_irq;
+
+    /* SPI/I2C interface mode */
+    struct
+    {
+        u8 addr;
+        u8 value;
+    } sim;
+
+    bool multi_read_bit;
+    unsigned int bootime;
+};
+
+/* Driver Data Structure */
+struct gsensor_data
+{
+    struct i2c_client *client;
+    struct device *dev;
+    struct mutex lock; /* Protects data access */
+
+    /* Sensor settings */
+    const struct gsensor_settings *sensor_settings;
+
+    /* Current configuration */
+    const struct gsensor_fs_avl *current_fullscale;
+    unsigned int odr;
+
+    /* Orientation data */
+    enum gsensor_orientation_hex orientation_hex;
+    char orientation_str[32];
+    struct delayed_work debounce_work;
+    enum gsensor_orientation_hex pending_orientation;
+    unsigned long last_change_jiffies;
+
+    /* State */
+    bool enabled;
+    bool initialized;
+    bool interrupt_mode;
+    bool irq_enabled;
+
+    /* IIO related */
+    struct iio_dev *indio_dev;
+
+    /* Verifone sysfs */
+    struct kobject *verifone_kobj;
+
+    /* Regulators (optional) */
+    struct regulator *vdd;
+    struct regulator *vdd_io;
+};
+
+/* Core functions */
+int gsensor_common_probe(struct iio_dev *indio_dev);
+void gsensor_common_remove(struct iio_dev *indio_dev);
+const struct gsensor_settings *gsensor_get_settings(const char *name);
+
+/* Buffer functions */
+int gsensor_allocate_ring(struct iio_dev *indio_dev);
+int gsensor_trig_set_state(struct iio_trigger *trig, bool state);
+
+int gsensor_allocate_trigger(struct iio_dev *indio_dev);
+extern const struct iio_trigger_ops gsensor_trigger_ops;
+
+/* Power management */
+int gsensor_power_enable(struct iio_dev *indio_dev);
+int gsensor_power_disable(struct iio_dev *indio_dev);
+
+/* I2C functions */
+int gsensor_i2c_read(struct device *dev, u8 reg, u8 *data, size_t len);
+int gsensor_i2c_write(struct device *dev, u8 reg, u8 value);
+int gsensor_init_main(void);
+void gsensor_exit_main(void);
+
+int gsensor_read_raw_data(struct gsensor_data *gs, int *x, int *y, int *z);
+int gsensor_set_enable(struct gsensor_data *gs, bool enable);
+int gsensor_set_dataready_irq(struct gsensor_data *gs, bool enable);
+
+/* Helper macros */
+#define GSENSOR_DEV_ATTR_SAMP_FREQ_AVAIL()                                     \
+    IIO_DEV_ATTR_SAMP_FREQ_AVAIL(gsensor_show_available_odr)
+
+#define GSENSOR_DEV_ATTR_SCALE_AVAIL()                                         \
+    IIO_DEVICE_ATTR(in_accel_scale_available, 0444,                            \
+                    gsensor_show_available_scale, NULL, 0)
+
+#endif /* __GSENSOR_H */
+

+ 11 - 0
main.c

@@ -30,6 +30,7 @@
 #include "power.h"
 #include "switches.h"
 #include "backlight.h"
+#include "gsensor.h"
 
 struct kobject *vfiec_kobj = NULL;
 
@@ -128,8 +129,17 @@ static int __init all_driver_init(void)
         printk(KERN_ERR "sssegment_init failed\n");
         goto out_led;
     }
+    ret = gsensor_init_main();
+    if (ret != 0) {
+        printk(KERN_ERR "gsensor_init failed\n");
+        goto out_ssegment;
+    }
+
     printk(KERN_INFO "all_driver_init\n");
     return ret;
+
+out_ssegment:
+    ssegment_exit();
 out_led:
     led_exit();
 out_light_ring:
@@ -178,6 +188,7 @@ static void __exit all_driver_exit(void)
     led_exit();
     ssegment_exit();
     kobject_put(vfiec_kobj);
+    gsensor_exit_main();
 }
 
 module_init(all_driver_init);

+ 45 - 0
test_app/test_gsensor.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+# test_gsensor.sh
+
+echo "=== Gsensor API Test ==="
+echo ""
+
+echo "1. Enable sensor:"
+echo 1 > /sys/kernel/vfiec/gsensor/enable
+echo "   enable = $(cat /sys/kernel/vfiec/gsensor/enable)"
+echo ""
+
+echo "2. Screen orientation (string):"
+echo "   $(cat /sys/kernel/vfiec/gsensor/screen_orientation)"
+echo ""
+
+echo "3. Instantaneous orientation (hex):"
+echo "   $(cat /sys/kernel/vfiec/gsensor/instantaneous_orientation)"
+echo ""
+
+echo "4. Mode:"
+echo "   $(cat /sys/kernel/vfiec/gsensor/mode)"
+echo ""
+
+echo "5. State:"
+echo "   $(cat /sys/kernel/vfiec/gsensor/state)"
+echo ""
+
+echo "6. Raw data (X Y Z mg):"
+echo "   $(cat /sys/kernel/vfiec/gsensor/raw_data)"
+echo ""
+
+echo "7. Test orientation change (rotate device now):"
+for i in {1..10}; do
+    echo -n "   [$i] "
+    cat /sys/kernel/vfiec/gsensor/screen_orientation
+    sleep 1
+done
+echo ""
+
+echo "8. Disable sensor:"
+echo 0 > /sys/kernel/vfiec/gsensor/enable
+echo "   enable = $(cat /sys/kernel/vfiec/gsensor/enable)"
+echo ""
+
+echo "=== Test completed ==="