#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }