|
|
@@ -0,0 +1,388 @@
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/fs.h>
|
|
|
+#include <linux/device.h>
|
|
|
+#include <linux/cdev.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/sched.h>
|
|
|
+#include <linux/timer.h>
|
|
|
+#include <linux/workqueue.h>
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+
|
|
|
+#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);
|
|
|
+ printk("%s %s %d.\n", __FILE__, __FUNCTION__, __LINE__);
|
|
|
+}
|
|
|
+
|
|
|
+static void heart_led_turn_off(void)
|
|
|
+{
|
|
|
+ oem_ec_write_ram(1, OFFSET_TURNOFF_CTL, 0x01);
|
|
|
+ printk("%s %s %d.\n", __FILE__, __FUNCTION__, __LINE__);
|
|
|
+}
|
|
|
+
|
|
|
+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))
|
|
|
+ {
|
|
|
+ count = sizeof(kbuff);
|
|
|
+ }
|
|
|
+ if (copy_from_user(kbuff, buf, count) != 0)
|
|
|
+ {
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ led_color = kbuff[0];
|
|
|
+ led_blink_flag = kbuff[1];
|
|
|
+ led_blink_speed = kbuff[2];
|
|
|
+ printk("%02x %02x %02x\n", kbuff[0], kbuff[1], kbuff[2]);
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+ 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
|
|
|
+ {
|
|
|
+ printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
|
|
+ heart_led_turn_off();
|
|
|
+ }
|
|
|
+ blink_count++;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ pr_info("led_heartbeat driver loaded, device number = %d:%d\n", led_heartbeat_MAJOR, led_heartbeat_MINOR);
|
|
|
+
|
|
|
+ // 初始化 cdev 并添加到系统
|
|
|
+ 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);
|
|
|
+
|
|
|
+ pr_info("led_heartbeat driver initialized successfully\n");
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void led_heartbeat_exit(void)
|
|
|
+{
|
|
|
+ dev_t dev_num = MKDEV(led_heartbeat_MAJOR, led_heartbeat_MINOR);
|
|
|
+
|
|
|
+ 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");
|
|
|
+}
|