Bmorn 1 сар өмнө
commit
93e62c30a3
6 өөрчлөгдсөн 938 нэмэгдсэн , 0 устгасан
  1. 17 0
      Makefile
  2. 103 0
      led.c
  3. 5 0
      led.h
  4. 754 0
      light_ring.c
  5. 5 0
      light_ring.h
  6. 54 0
      main.c

+ 17 - 0
Makefile

@@ -0,0 +1,17 @@
+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
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+ 
+EXTRA_CFLAGS +=-g
+ 
+PWD = $(shell pwd)
+all:
+	make -C $(KERNELDIR) M=$(PWD) modules
+clean:
+	rm -rf *.o
+	rm *.ko *.order *.mod.c *.symvers

+ 103 - 0
led.c

@@ -0,0 +1,103 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include "light_ring.h"
+
+
+extern struct kobject *vfiec_kobj;
+static struct kobject *led_kobj = NULL;
+
+
+
+/* ==================== color ==================== */
+static ssize_t color_show(struct kobject *kobj, struct kobj_attribute *attr,
+                          char *buf)
+{
+    static int count = 0;
+    count++;
+    return sprintf(buf, "color_show count=%d\n", count);
+}
+
+static ssize_t color_store(struct kobject *kobj, struct kobj_attribute *attr,
+                           const char *buf, size_t count)
+{
+    printk("color_store kernel rev:%s\n", buf);
+
+    return count;
+}
+
+static struct kobj_attribute color_attr =
+    __ATTR(color, 0644, color_show, color_store);
+
+
+/* ==================== mode ==================== */
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+                         char *buf)
+{
+    static int count = 0;
+    count++;
+
+    return sprintf(buf, "mode_show count=%d\n", count);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
+                          const char *buf, size_t count)
+{
+    printk("mode_store kernel rev:%s\n", buf);
+    return count;
+}
+
+static struct kobj_attribute mode_attr =
+    __ATTR(mode, 0644, mode_show, mode_store);
+
+/* ==================== 属性组 ==================== */
+static struct attribute *led_attrs[] = {
+    &color_attr.attr,
+    &mode_attr.attr,
+    NULL,
+};
+
+static struct attribute_group led_attr_group = {
+    .attrs = led_attrs,
+};
+
+int led_init(void)
+{
+    int ret;
+        /* 创建 /sys/kernel/vfiec/lightring */
+    led_kobj = kobject_create_and_add("led", vfiec_kobj);
+    if (!led_kobj)
+    {
+        ret = -ENOMEM;
+    }
+
+    /* 创建属性文件 */
+    ret = sysfs_create_group(led_kobj, &led_attr_group);
+    if (ret)
+    {
+        pr_err("Failed to create sysfs group: %d\n", ret);
+    }
+
+    return ret;
+}
+
+void led_exit(void)
+{
+    sysfs_remove_group(led_kobj, &led_attr_group);
+    kobject_put(led_kobj);
+}
+

+ 5 - 0
led.h

@@ -0,0 +1,5 @@
+#ifndef __LED_H__
+#define __LED_H__
+int led_init(void);
+void led_exit(void);
+#endif

+ 754 - 0
light_ring.c

@@ -0,0 +1,754 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#define PCA9685_ADDR 0x40
+
+/* I2C设备地址 */
+#define PCA9685_DEFAULT_ADDR 0x40
+#define PCA9685_GENERAL_CALL 0x00
+
+/* PCA9685寄存器定义 */
+#define PCA9685_MODE1 0x00
+#define PCA9685_MODE2 0x01
+#define PCA9685_SUBADR1 0x02
+#define PCA9685_SUBADR2 0x03
+#define PCA9685_SUBADR3 0x04
+#define PCA9685_ALLCALLADR 0x05
+#define PCA9685_LED0_ON_L 0x06
+#define PCA9685_LED0_ON_H 0x07
+#define PCA9685_LED0_OFF_L 0x08
+#define PCA9685_LED0_OFF_H 0x09
+#define PCA9685_ALL_LED_ON_L 0xFA
+#define PCA9685_ALL_LED_ON_H 0xFB
+#define PCA9685_ALL_LED_OFF_L 0xFC
+#define PCA9685_ALL_LED_OFF_H 0xFD
+#define PCA9685_PRESCALE 0xFE
+#define PCA9685_TESTMODE 0xFF
+
+/* MODE1寄存器位定义 */
+#define MODE1_RESTART 0x80
+#define MODE1_EXTCLK 0x40
+#define MODE1_AI 0x20
+#define MODE1_SLEEP 0x10
+#define MODE1_SUB1 0x08
+#define MODE1_SUB2 0x04
+#define MODE1_SUB3 0x02
+#define MODE1_ALLCALL 0x01
+
+/* MODE2寄存器位定义 */
+#define MODE2_INVRT 0x10
+#define MODE2_OCH 0x08
+#define MODE2_OUTDRV 0x04
+#define MODE2_OUTNE1 0x02
+#define MODE2_OUTNE0 0x01
+
+/* 参数 */
+#define PCA9685_PWM_RESOLUTION 4096
+#define PCA9685_OSC_FREQ 25000000
+#define PCA9685_MIN_FREQ 24
+#define PCA9685_MAX_FREQ 1526
+
+
+/* 错误码 */
+#define PCA9685_OK              0
+#define PCA9685_ERR_OPEN       -1
+#define PCA9685_ERR_ADDR       -2
+#define PCA9685_ERR_WRITE      -3
+#define PCA9685_ERR_READ       -4
+#define PCA9685_ERR_PARAM      -5
+#define PCA9685_ERR_INIT       -6
+
+
+#define COLOR_RED 0xff0000     //{255, 0, 0}
+#define COLOR_GREEN 0x00ff00   //{0, 255, 0}
+#define COLOR_BLUE 0x0000ff    //{0, 0, 255}
+#define COLOR_WHITE 0xffffff   //{255, 255, 255}
+#define COLOR_FUCHSIA 0xff00ff //{255, 0, 255}
+#define COLOR_YELLOW 0xffff00  //{255, 255, 0}
+#define COLOR_CYAN 0x00ffff    //{0, 255, 255}
+
+#define LIGHT_MODE_OFF  0x01
+#define LIGHT_MODE_ON   0x02
+#define LIGHT_MODE_FLASH_1SEC 0x03
+#define LIGHT_MODE_FLASH_2SEC 0x04
+#define LIGHT_MODE_FLASH_3SEC 0x05
+#define LIGHT_MODE_FLASH_SLOW 0x06
+#define LIGHT_MODE_FLASH_MEDIUM 0x07
+#define LIGHT_MODE_FLASH_FAST 0x08
+
+/* 内部数据结构 */
+struct lightring_data
+{
+    u32 brightness;
+    u32 color;
+    u32 max_fade_brightness;
+    u32 mode;
+    u32 flash_time;
+};
+
+/* 驱动私有数据结构 */
+struct light_ring_i2c_dev
+{
+    struct pci_dev *pdev;             /* PCI设备指针 */
+    struct i2c_adapter *adapter;      /* I2C适配器指针 */
+    struct i2c_client client;         /* I2C客户端指针 */
+    struct cdev cdev;                 /* 字符设备结构 */
+    struct class *class;              /* 设备类 */
+    struct device *device;            /* 设备指针 */
+    dev_t dev_num;                    /* 设备号 */
+    struct mutex lock;                /* 互斥锁 */
+    int bus_number;                   /* 保存的总线编号 */
+    struct lightring_data *lightring; /* 内部数据结构 */
+    struct delayed_work delay_work1;
+};
+int set_color(unsigned int color);
+static struct light_ring_i2c_dev *global_dev = NULL;
+
+static void delay_work_func(struct work_struct *work)
+{
+    static int flag = 0;
+    printk("this is delay_work_func !\n");
+    //自己再启动自己,也可以在其他地方启动
+    if(global_dev->lightring->flash_time != 0)
+    {
+        if(flag == 0)
+        {
+            // 显示颜色
+            set_color(global_dev->lightring->color);
+            flag = 1;
+        }
+        else
+        {
+            // 关闭灯带
+            set_color(0);
+            flag = 0;
+        }
+	    schedule_delayed_work(&global_dev->delay_work1, msecs_to_jiffies(global_dev->lightring->flash_time));
+    }
+}
+
+// s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value);
+// s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);
+
+int set_color(unsigned int color)
+{
+    int i = 0;
+    int ret = 0;
+    unsigned char red;
+    unsigned char green;
+    unsigned char blue;
+
+    unsigned char red_pwm_l = 0;
+    unsigned char red_pwm_h = 0;
+    unsigned char green_pwm_l = 0;
+    unsigned char green_pwm_h = 0;
+    unsigned char blue_pwm_l = 0;
+    unsigned char blue_pwm_h = 0;
+
+    red = 255 - ((color >> 16) & 0xff);
+    green = 255 - ((color >> 8) & 0xff);
+    blue = 255 - (color & 0xff);
+
+    red_pwm_l = (red * 4095 / 255) & 0xff;
+    red_pwm_h = ((red * 4095 / 255) >> 8) & 0x0f;
+    green_pwm_l = (green * 4095 / 255) & 0xff;
+    green_pwm_h = ((green * 4095 / 255) >> 8) & 0x0f;
+    blue_pwm_l = (blue * 4095 / 255) & 0xff;
+    blue_pwm_h = ((blue * 4095 / 255) >> 8) & 0x0f;
+
+    for (i = 0; i < 3; i++)
+    {
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x06 + i * 12, 0);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x07 + i * 12, 0);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x08 + i * 12, red_pwm_l);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x09 + i * 12, red_pwm_h);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x0a + i * 12, 0);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x0b + i * 12, 0);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x0c + i * 12, green_pwm_l);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x0d + i * 12, green_pwm_h);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x0e + i * 12, 0);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x0f + i * 12, 0);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x10 + i * 12, blue_pwm_l);
+        ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x11 + i * 12, blue_pwm_h);
+    }
+    if(ret != 0)
+    {
+        printk("set color failed\n");
+        return -1;
+    }
+    return 0;
+}
+
+static ssize_t brightness_show(struct kobject *kobj, struct kobj_attribute *attr,
+                               char *buf)
+{
+    return sprintf(buf, "%u\n", global_dev->lightring->brightness);
+}
+
+static ssize_t brightness_store(struct kobject *kobj, struct kobj_attribute *attr,
+                                const char *buf, size_t count)
+{
+    u32 val;
+    int ret;
+
+    ret = kstrtou32(buf, 10, &val);
+    if (ret < 0)
+        return ret;
+
+    /* 硬件操作:设置亮度 */
+    global_dev->lightring->brightness = val;
+    pr_info("Lightring: brightness set to %u\n", val);
+
+    return count;
+}
+
+static struct kobj_attribute brightness_attr =
+    __ATTR(brightness, 0644, brightness_show, brightness_store);
+
+/* ==================== color ==================== */
+static ssize_t color_show(struct kobject *kobj, struct kobj_attribute *attr,
+                          char *buf)
+{
+    return sprintf(buf, "0x%06x\n", global_dev->lightring->color);
+}
+
+static ssize_t color_store(struct kobject *kobj, struct kobj_attribute *attr,
+                           const char *buf, size_t count)
+{
+    u32 val;
+    int ret;
+
+    if (strncmp(buf, "White", 5) == 0)
+    {
+        printk("Lightring: color set to White\n");
+        val = COLOR_WHITE;
+    }
+    else if (strncmp(buf, "Red", 3) == 0)
+    {
+        printk("Lightring: color set to Red\n");
+        val = COLOR_RED;
+    }
+    else if (strncmp(buf, "Green", 5) == 0)
+    {
+        printk("Lightring: color set to Green\n");
+        val = COLOR_GREEN;
+    }
+    else if (strncmp(buf, "Blue", 4) == 0)
+    {
+        printk("Lightring: color set to Blue\n");
+        val = COLOR_BLUE;
+    }
+    else if (strncmp(buf, "Fuchsia", 7) == 0)
+    {
+        printk("Lightring: color set to Fuchsia\n");
+        val = COLOR_FUCHSIA;
+    }
+    else if (strncmp(buf, "Yellow", 6) == 0)
+    {
+        printk("Lightring: color set to Yellow\n");
+        val = COLOR_YELLOW;
+    }
+    else if (strncmp(buf, "Cyan", 4) == 0)
+    {
+        printk("Lightring: color set to Cyan\n");
+        val = COLOR_CYAN;
+    }
+    else
+    {
+        printk("Lightring: color format error\n");
+        return -1;
+    }
+
+    global_dev->lightring->color = val;
+
+    ret = set_color(val);
+    if (ret < 0)
+    {
+        pr_err("Lightring: set color failed\n");
+        return ret;
+    }
+
+    pr_info("Lightring: color set to 0x%06x\n", val);
+
+    return count;
+}
+
+static struct kobj_attribute color_attr =
+    __ATTR(color, 0644, color_show, color_store);
+
+/* ==================== max_fade_brightness ==================== */
+static ssize_t max_fade_brightness_show(struct kobject *kobj,
+                                        struct kobj_attribute *attr,
+                                        char *buf)
+{
+    return sprintf(buf, "%u\n", global_dev->lightring->max_fade_brightness);
+}
+
+static ssize_t max_fade_brightness_store(struct kobject *kobj,
+                                         struct kobj_attribute *attr,
+                                         const char *buf, size_t count)
+{
+    u32 val;
+    int ret;
+
+    ret = kstrtou32(buf, 10, &val);
+    if (ret < 0)
+        return ret;
+
+    global_dev->lightring->max_fade_brightness = val;
+    pr_info("Lightring: max_fade_brightness set to %u\n", val);
+
+    return count;
+}
+
+static struct kobj_attribute max_fade_brightness_attr =
+    __ATTR(max_fade_brightness, 0644, max_fade_brightness_show,
+           max_fade_brightness_store);
+
+/* ==================== mode ==================== */
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+                         char *buf)
+{
+    const char *mode_str;
+
+    switch (global_dev->lightring->mode)
+    {
+    case 0:
+        mode_str = "static";
+        break;
+    case 1:
+        mode_str = "breathing";
+        break;
+    case 2:
+        mode_str = "rainbow";
+        break;
+    case 3:
+        mode_str = "fade";
+        break;
+    default:
+        mode_str = "unknown";
+        break;
+    }
+
+    return sprintf(buf, "%s (%u)\n", mode_str, global_dev->lightring->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
+                          const char *buf, size_t count)
+{
+    u32 val;
+    int ret;
+    int mode = 0;
+    int time = 0;
+
+    
+
+    if (strncmp(buf, "off", 3) == 0)
+    {
+        printk("Lightring: mode set to off\n");
+        mode = LIGHT_MODE_OFF;
+        time = 0;
+    }
+    else if (strncmp(buf, "on", 2) == 0)
+    {
+        printk("Lightring: mode set to on\n");
+        mode = LIGHT_MODE_ON;
+        time = 0;
+    }
+    else if (strncmp(buf, "flash_1sec", 10) == 0)
+    {
+        printk("Lightring: mode set to flash_1sec\n");
+        mode = LIGHT_MODE_FLASH_1SEC;
+        time = 1000;
+    }
+    else if (strncmp(buf, "flash_2sec", 10) == 0)
+    {
+        printk("Lightring: mode set to flash_2sec\n");
+        mode = LIGHT_MODE_FLASH_2SEC;
+        time = 2000;
+    }
+    else if (strncmp(buf, "flash_3sec", 10) == 0)
+    {
+        printk("Lightring: mode set to flash_3sec\n");
+        mode = LIGHT_MODE_FLASH_3SEC;
+        time = 3000;
+    }
+    else if (strncmp(buf, "flash_slow", 10) == 0)
+    {
+        printk("Lightring: mode set to flash_slow\n");
+        mode = LIGHT_MODE_FLASH_SLOW;
+        time = 4000;
+    }
+    else if (strncmp(buf, "flash_medium", 12) == 0)
+    {
+        printk("Lightring: mode set to flash_medium\n");
+        mode = LIGHT_MODE_FLASH_MEDIUM;
+        time = 2000;
+    }
+    else if (strncmp(buf, "flash_fast", 10) == 0)
+    {
+        printk("Lightring: mode set to flash_fast\n");
+        mode = LIGHT_MODE_FLASH_FAST;
+        time = 200;
+    }
+    else
+    {
+        printk("Lightring: mode format error\n");
+        return -1;
+    }
+
+    global_dev->lightring->mode = mode;
+    global_dev->lightring->flash_time = time;
+    if(time != 0)
+    {
+        schedule_delayed_work(&global_dev->delay_work1, msecs_to_jiffies(global_dev->lightring->flash_time));
+    }
+
+    pr_info("global_dev->lightring: mode set to %u :flash_time=%d\n", global_dev->lightring->mode, global_dev->lightring->flash_time);
+
+    return count;
+}
+
+static struct kobj_attribute mode_attr =
+    __ATTR(mode, 0644, mode_show, mode_store);
+
+/* ==================== 属性组 ==================== */
+static struct attribute *lightring_attrs[] = {
+    &brightness_attr.attr,
+    &color_attr.attr,
+    &max_fade_brightness_attr.attr,
+    &mode_attr.attr,
+    NULL,
+};
+
+static struct attribute_group lightring_attr_group = {
+    .attrs = lightring_attrs,
+};
+
+// static struct kobject *vfiec_kobj;
+extern struct kobject *vfiec_kobj;
+static struct kobject *lightring_kobj;
+
+static struct i2c_adapter *find_i2c_adapter_by_pci(struct pci_dev *pdev)
+{
+    struct i2c_adapter *adapter = NULL;
+    struct device *dev;
+    struct fwnode_handle *fwnode;
+
+    if (!pdev)
+        return NULL;
+
+    /* 获取PCI设备的fwnode */
+    fwnode = dev_fwnode(&pdev->dev);
+    if (!fwnode)
+    {
+        pr_err("PCI device has no fwnode\n");
+        return NULL;
+    }
+
+    /* 对于内核5.15,需要使用其他方法查找i2c适配器 */
+
+    /* 方法A: 通过设备链接查找 */
+    struct device_link *link;
+
+    /* 遍历PCI设备的消费者链接 */
+    list_for_each_entry(link, &pdev->dev.links.consumers, s_node)
+    {
+        if (link->supplier == &pdev->dev)
+        {
+            /* 检查消费者是否是i2c适配器 */
+            if (link->consumer->type &&
+                link->consumer->type->name &&
+                strcmp(link->consumer->type->name, "i2c_adapter") == 0)
+            {
+                adapter = i2c_verify_adapter(link->consumer);
+                if (adapter)
+                    break;
+            }
+        }
+    }
+
+    if (adapter)
+    {
+        i2c_put_adapter(adapter);               /* 先释放引用 */
+        adapter = i2c_get_adapter(adapter->nr); /* 重新获取 */
+        printk("i2c_get_adapter\n");
+        return adapter;
+    }
+
+    /* 方法B: 遍历所有i2c适配器 */
+    int i;
+    for (i = 0; i < 255; i++)
+    {
+        adapter = i2c_get_adapter(i);
+        if (adapter)
+        {
+            /* 检查适配器的父设备是否是我们的PCI设备 */
+            if (adapter->dev.parent == &pdev->dev)
+            {
+                return adapter;
+            }
+            i2c_put_adapter(adapter);
+        }
+    }
+
+    return NULL;
+}
+
+/* 初始化PCI设备 */
+static struct pci_dev *init_pci_device(void)
+{
+    struct pci_dev *pdev;
+
+    /* 查找PCI设备: domain=0, bus=0, devfn=PCI_DEVFN(0x1f, 4) */
+    pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x1f, 4));
+    if (!pdev)
+    {
+        pr_err("Failed to find PCI device 0000:00:1f.4\n");
+        return NULL;
+    }
+
+    pr_info("Found PCI device: %04x:%04x\n", pdev->vendor, pdev->device);
+    return pdev;
+}
+
+int pca9685_all_off(void)
+{
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_ALL_LED_ON_L, 0) < 0)
+        return PCA9685_ERR_WRITE;
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_ALL_LED_ON_H, 0) < 0)
+        return PCA9685_ERR_WRITE;
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_ALL_LED_OFF_L, 0) < 0)
+        return PCA9685_ERR_WRITE;
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_ALL_LED_OFF_H, 0x10) < 0)
+        return PCA9685_ERR_WRITE;
+    return PCA9685_OK;
+}
+
+int pca9685_set_open_drain(bool enable)
+{
+    uint8_t mode2;
+    int ret;
+    ret = i2c_smbus_read_byte_data(&global_dev->client, PCA9685_MODE2);
+    if (ret < 0)
+        return PCA9685_ERR_READ;
+    mode2 = ret & 0xff;
+    if (enable)
+        mode2 &= ~MODE2_OUTDRV;
+    else
+        mode2 |= MODE2_OUTDRV;
+    return i2c_smbus_write_byte_data(&global_dev->client, PCA9685_MODE2, mode2);
+}
+
+int pca9685_set_pwm_freq(int freq)
+{
+    int ret = 0;
+    uint8_t prescale, old_mode, new_mode;
+    int prescale_val = (PCA9685_OSC_FREQ / (4096 * freq)) - 1;
+    prescale = (uint8_t)(prescale_val);
+    if (prescale < 3)
+        prescale = 3;
+
+    ret = i2c_smbus_read_byte_data(&global_dev->client, PCA9685_MODE1);
+    if (ret < 0)
+        return PCA9685_ERR_READ;
+    old_mode = ret & 0xff;
+    new_mode = (old_mode & ~MODE1_RESTART) | MODE1_SLEEP;
+
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_MODE1, new_mode) < 0)
+        return PCA9685_ERR_WRITE;
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_PRESCALE, prescale) < 0)
+        return PCA9685_ERR_WRITE;
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_MODE1, old_mode) < 0)
+        return PCA9685_ERR_WRITE;
+
+    udelay(5000);
+    if (i2c_smbus_write_byte_data(&global_dev->client, PCA9685_MODE1, old_mode | MODE1_RESTART | MODE1_AI) < 0)
+    {
+        return PCA9685_ERR_WRITE;
+    }
+    return 0;
+}
+
+void init_pca9685(void)
+{
+    int ret = 0;
+    // reset
+    ret |= i2c_smbus_write_byte_data(&global_dev->client, 0x00, 0x06);
+    udelay(10000);
+
+    ret |= i2c_smbus_write_byte_data(&global_dev->client, PCA9685_MODE2, MODE2_OUTDRV | MODE2_OCH);
+    ret |= i2c_smbus_write_byte_data(&global_dev->client, PCA9685_MODE1, MODE1_RESTART | MODE1_AI | MODE1_ALLCALL);
+    printk("init_pca9685: %d\n", ret);
+
+    ret = pca9685_set_pwm_freq(50);
+    if (ret != 0)
+    {
+        printk("pca9685_set_pwm_freq failed: %d\n", ret);
+    }
+    ret = pca9685_all_off();
+    if (ret != 0)
+    {
+        printk("pca9685_all_off failed: %d\n", ret);
+    }
+
+    ret = pca9685_set_open_drain(true);
+    if (ret != 0)
+    {
+        printk("pca9685_set_open_drain failed: %d\n", ret);
+    }
+}
+int light_ring_init(void)
+{
+    int ret;
+    struct i2c_board_info info;
+    struct light_ring_i2c_dev *dev;
+
+    pr_info("light_ring_i2c_dev initializing...\n");
+
+    /* 分配设备结构 */
+    dev = kzalloc(sizeof(struct light_ring_i2c_dev), GFP_KERNEL);
+    if (!dev)
+    {
+        pr_err("Failed to allocate device structure\n");
+        return -ENOMEM;
+    }
+    global_dev = dev;
+    mutex_init(&dev->lock);
+
+    /* 1. 初始化PCI设备 */
+    dev->pdev = init_pci_device();
+    if (!dev->pdev)
+    {
+        pr_err("Failed to initialize PCI device\n");
+        ret = -ENODEV;
+        goto err_free_dev;
+    }
+
+    /* 2. 通过PCI设备查找I2C适配器 */
+    dev->adapter = find_i2c_adapter_by_pci(dev->pdev);
+    if (!dev->adapter)
+    {
+        pr_err("Failed to find I2C adapter\n");
+        ret = -ENODEV;
+        goto err_put_pci;
+    }
+
+    /* 保存总线编号用于调试 */
+    dev->bus_number = dev->adapter->nr;
+    pr_info("Found I2C adapter on bus %d\n", dev->bus_number);
+
+    // 检查适配器是否支持字节数据读写
+    if (!i2c_check_functionality(dev->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA |
+                                                   I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+    {
+        dev_err(&dev->adapter->dev, "Adapter does not support required SMBus features\n");
+        return -ENOTSUPP; // 返回不支持错误码
+    }
+    else
+    {
+        pr_info("Adapter supports required SMBus features\n");
+    }
+
+    /* 初始化I2C客户端 */
+    dev->client.adapter = dev->adapter;      // 你已有的 adapter
+    dev->client.addr = PCA9685_DEFAULT_ADDR; // 从设备地址
+    dev->client.flags = 0;                   // 7位地址模式
+
+    INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
+
+    init_pca9685();
+
+    global_dev->lightring = kzalloc(sizeof(*global_dev->lightring), GFP_KERNEL);
+    if (!global_dev->lightring)
+    {
+        pr_err("Failed to allocate lightring structure\n");
+        ret = -ENOMEM;
+        goto err_put_pci;
+    }
+
+    /* 默认值 */
+    global_dev->lightring->brightness = 128;
+    global_dev->lightring->color = 0xFFFFFF; /* 白色 */
+    global_dev->lightring->max_fade_brightness = 255;
+    global_dev->lightring->mode = 0; /* static */
+
+    /* 创建 /sys/kernel/vfiec */
+    // vfiec_kobj = kobject_create_and_add("vfiec", kernel_kobj);
+    // if (!vfiec_kobj)
+    // {
+    //     ret = -ENOMEM;
+    //     goto err_free;
+    // }
+
+    /* 创建 /sys/kernel/vfiec/lightring */
+    lightring_kobj = kobject_create_and_add("lightring", vfiec_kobj);
+    if (!lightring_kobj)
+    {
+        ret = -ENOMEM;
+        goto err_free;
+    }
+
+    /* 创建属性文件 */
+    ret = sysfs_create_group(lightring_kobj, &lightring_attr_group);
+    if (ret)
+    {
+        pr_err("Failed to create sysfs group: %d\n", ret);
+        goto err_lightring;
+    }
+
+    return 0;
+err_lightring:
+    kobject_put(lightring_kobj);
+// err_vfiec:
+//     kobject_put(vfiec_kobj);
+err_free:
+    kfree(global_dev->lightring);
+err_put_pci:
+    pci_dev_put(dev->pdev);
+err_free_dev:
+    kfree(dev);
+    global_dev = NULL;
+    return ret;
+}
+
+void light_ring_exit(void)
+{
+    struct light_ring_i2c_dev *dev = global_dev;
+    global_dev->lightring->flash_time = 0;
+    cancel_delayed_work_sync(&global_dev->delay_work1);
+    sysfs_remove_group(lightring_kobj, &lightring_attr_group);
+    kobject_put(lightring_kobj);
+    // kobject_put(vfiec_kobj);
+    kfree(global_dev->lightring);
+    printk(KERN_INFO "LED module unloaded\n");
+    if (dev->adapter)
+        i2c_put_adapter(dev->adapter);
+
+    if (dev->pdev)
+        pci_dev_put(dev->pdev);
+
+    kfree(dev);
+    global_dev = NULL;
+}
+

+ 5 - 0
light_ring.h

@@ -0,0 +1,5 @@
+#ifndef __LIGHT_RING_H__
+#define __LIGHT_RING_H__
+int light_ring_init(void);
+void light_ring_exit(void);
+#endif

+ 54 - 0
main.c

@@ -0,0 +1,54 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include "light_ring.h"
+#include "led.h"
+
+struct kobject *vfiec_kobj = NULL;
+
+static int __init all_driver_init(void)
+{
+    int ret = 0;
+    /* 创建 /sys/kernel/vfiec */
+    vfiec_kobj = kobject_create_and_add("vfiec", kernel_kobj);
+    if (!vfiec_kobj)
+    {
+        ret = -ENOMEM;
+    }
+
+    light_ring_init();
+    led_init();
+    printk(KERN_INFO "all_driver_init\n");
+
+    return ret;
+}
+
+static void __exit all_driver_exit(void)
+{
+    led_exit();
+    light_ring_exit();
+    kobject_put(vfiec_kobj);
+}
+
+module_init(all_driver_init);
+module_exit(all_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Your Name");
+MODULE_DESCRIPTION("Fixed I2C Bus Driver for Intel N97 (0000:00:1f.4)");
+MODULE_VERSION("1.0");