#include #include #include #include #include #include #include #include #include #include #include #include "gpioregs.h" #define DEVICE_NAME "led" #define CLASS_NAME "led_heartbeat_class" #define led_heartbeat_MAJOR 56 #define led_heartbeat_MINOR 60 typedef enum { LED_GREEN = 0, LED_RED = 1 } led_color_t; static struct class *led_heartbeat_class = NULL; static struct device *led_heartbeat_device = NULL; static struct cdev led_heartbeat_cdev; static int led_color = 0; static int led_blink_flag = 0; static int led_blink_speed = 0; static unsigned char blink_count = 0; static struct delayed_work delay_work1; static int wait_ibf(void) { int i = 0; while (inb(EC_CMD_PORT) & EC_IBF) { if (++i > TIMEOUT_LOOPS) { return -1; } udelay(1); } return 0; } static int wait_obf(void) { int i = 0; while (!(inb(EC_CMD_PORT) & EC_OBF)) { if (++i > TIMEOUT_LOOPS) { return -1; } udelay(1); } return 0; } static int ec_read_ram(uint8_t offset, uint8_t *data) { if (wait_ibf() < 0) return -1; outb(CMD_READ_RAM, EC_CMD_PORT); if (wait_ibf() < 0) return -1; outb(offset, EC_DATA_PORT); if (wait_obf() < 0) return -1; *data = inb(EC_DATA_PORT); return 0; } static int ec_write_ram(uint8_t offset, uint8_t data) { if (wait_ibf() < 0) return -1; outb(CMD_WRITE_RAM, EC_CMD_PORT); if (wait_ibf() < 0) return -1; outb(offset, EC_DATA_PORT); if (wait_ibf() < 0) return -1; outb(data, EC_DATA_PORT); return 0; } static int oem_ec_read_ram(uint8_t page, uint8_t offset, uint8_t *data) { unsigned char WEC, REC; switch(page) { case 0: { WEC = 0x96; REC = 0x95; break; } case 1: { WEC = 0x98; REC = 0x97; break; } default: { WEC = 0x81; REC = 0x80; break; } } if (wait_ibf() < 0) return -1; outb(REC, EC_CMD_PORT); if (wait_ibf() < 0) return -1; outb(offset, EC_DATA_PORT); if (wait_obf() < 0) return -1; *data = inb(EC_DATA_PORT); return 0; } static int oem_ec_write_ram(uint8_t page, uint8_t offset, uint8_t data) { unsigned char WEC, REC; switch(page) { case 0: { WEC = 0x96; REC = 0x95; break; } case 1: { WEC = 0x98; REC = 0x97; break; } default: { WEC = 0x81; REC = 0x80; break; } } if (wait_ibf() < 0) return -1; outb(WEC, EC_CMD_PORT); if (wait_ibf() < 0) return -1; outb(offset, EC_DATA_PORT); if (wait_ibf() < 0) return -1; outb(data, EC_DATA_PORT); return 0; } static void heart_led_turn_on(void) { oem_ec_write_ram(1, OFFSET_TURNOFF_CTL, 0x00); } static void heart_led_turn_off(void) { oem_ec_write_ram(1, OFFSET_TURNOFF_CTL, 0x01); } static int led_set_color(led_color_t color) { uint8_t val; if (ec_read_ram(OFFSET_COLOR_CTL, &val) < 0) return -1; if (color == LED_RED) val |= BIT0; else val &= ~BIT0; heart_led_turn_on(); return ec_write_ram(OFFSET_COLOR_CTL, val); } static int led_set_blink(int enable, uint8_t interval_unit) { uint8_t ctl; if (enable) { if (ec_write_ram(OFFSET_BLINK_TIME, interval_unit) < 0) return -1; } if (ec_read_ram(OFFSET_BLINK_CTL, &ctl) < 0) return -1; if (enable) ctl |= BIT0; else ctl &= ~BIT0; heart_led_turn_on(); return ec_write_ram(OFFSET_BLINK_CTL, ctl); } static int led_heartbeat_open(struct inode *inode, struct file *filp) { return 0; } static int led_heartbeat_release(struct inode *inode, struct file *filp) { return 0; } static ssize_t led_heartbeat_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; return ret; } static ssize_t led_heartbeat_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { unsigned char kbuff[3] = {0}; if(count != sizeof(kbuff)) { return -EINVAL; } if (copy_from_user(kbuff, buf, count) != 0) { return -EFAULT; } led_color = kbuff[0]; led_blink_flag = kbuff[1]; led_blink_speed = kbuff[2]; if(led_color != 0x0 && led_color != 0x40 && led_color != 0x80) { return -EINVAL; } if(led_blink_flag != 0x0 && led_blink_flag != 0x1) { return -EINVAL; } if(led_color == 0x00) { // off heart_led_turn_off(); return count; } else if(led_color == 0x40) { // set color to red led_set_color(LED_RED); } else if(led_color == 0x80) { // set color to green led_set_color(LED_GREEN); } // led_set_blink(led_blink_flag, kbuff[2]); // return count; // The following code is reserved because the blink cycle of EC is incorrect. If the EC cannot be changed, we have to delay the work queue. if(led_blink_flag) { // led_set_blink(1, led_blink_speed); blink_count = 0; schedule_delayed_work(&delay_work1, msecs_to_jiffies(1000*led_blink_speed)); } return count; } static struct file_operations led_heartbeat_fops = { .owner = THIS_MODULE, .open = led_heartbeat_open, .release = led_heartbeat_release, .read = led_heartbeat_read, .write = led_heartbeat_write, }; static void led_heartbeat_work_func(struct work_struct *work) { if(led_blink_flag) { blink_count++; if(blink_count%2 == 0) { if(led_color == 0x40) { // set color to red led_set_color(LED_RED); } else if(led_color == 0x80) { // set color to green led_set_color(LED_GREEN); } } else { heart_led_turn_off(); } schedule_delayed_work(&delay_work1, msecs_to_jiffies(1000*led_blink_speed)); } } int led_heartbeat_init(void) { dev_t dev_num; int ret; dev_num = MKDEV(led_heartbeat_MAJOR, led_heartbeat_MINOR); ret = register_chrdev_region(dev_num, 1, DEVICE_NAME); if (ret < 0) { pr_err("Failed_heartbeat to register device number %d:%d\n", led_heartbeat_MAJOR, led_heartbeat_MINOR); return ret; } cdev_init(&led_heartbeat_cdev, &led_heartbeat_fops); led_heartbeat_cdev.owner = THIS_MODULE; ret = cdev_add(&led_heartbeat_cdev, dev_num, 1); if (ret < 0) { pr_err("Failed_heartbeat to add cdev\n"); unregister_chrdev_region(dev_num, 1); return ret; } led_heartbeat_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(led_heartbeat_class)) { pr_err("Failed_heartbeat to create class\n"); cdev_del(&led_heartbeat_cdev); unregister_chrdev_region(dev_num, 1); return PTR_ERR(led_heartbeat_class); } led_heartbeat_device = device_create(led_heartbeat_class, NULL, dev_num, NULL, DEVICE_NAME); if (IS_ERR(led_heartbeat_device)) { pr_err("Failed_heartbeat to create device\n"); class_destroy(led_heartbeat_class); cdev_del(&led_heartbeat_cdev); unregister_chrdev_region(dev_num, 1); return PTR_ERR(led_heartbeat_device); } INIT_DELAYED_WORK(&delay_work1, led_heartbeat_work_func); return 0; } void led_heartbeat_exit(void) { dev_t dev_num = MKDEV(led_heartbeat_MAJOR, led_heartbeat_MINOR); cancel_delayed_work_sync(&delay_work1); device_destroy(led_heartbeat_class, dev_num); class_destroy(led_heartbeat_class); cdev_del(&led_heartbeat_cdev); unregister_chrdev_region(dev_num, 1); pr_info("led_heartbeat driver unloaded\n"); }