Browse Source

添加power interface驱动

liu qidong [url ssh://qidong.liu@10.2.90.253:29418/] 1 tuần trước cách đây
mục cha
commit
5ff259bfdc
5 tập tin đã thay đổi với 410 bổ sung217 xóa
  1. 5 4
      main.c
  2. 326 210
      power.c
  3. 3 2
      power.h
  4. 2 1
      test_app/build.sh
  5. 74 0
      test_app/power_app.c

+ 5 - 4
main.c

@@ -45,6 +45,7 @@ static int __init all_driver_init(void)
         return ret;
     }
 
+
     ret = backlight_init();
     if(ret != 0)
     {
@@ -57,10 +58,10 @@ static int __init all_driver_init(void)
         printk(KERN_ERR "switches_init failed\n");
         goto out_backlight;
     }
-    ret = power_init();
+    ret = power_interface_init();
     if(ret != 0)
     {
-        printk(KERN_ERR "power_init failed\n");
+        printk(KERN_ERR "power_interface_init failed\n");
         goto out_switches;
     }
     ret = watchdog_init();
@@ -161,7 +162,7 @@ out_batteryled:
 out_watchdog:
     watchdog_exit();
 out_power:
-    power_exit();
+    power_interface_exit();
 out_switches:
     switches_exit();
 out_backlight:
@@ -175,7 +176,7 @@ static void __exit all_driver_exit(void)
 {
     backlight_exit();
     switches_exit();
-    power_exit();
+    power_interface_exit();
     watchdog_exit();
     batteryled_exit();
     cashd_exit();

+ 326 - 210
power.c

@@ -1,289 +1,405 @@
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/cdev.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
-#include <linux/device.h>
-#include <linux/stat.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
 #include <linux/poll.h>
+#include <linux/delay.h>
 
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 
 #include <linux/io.h>
+
 #include "gpioregs.h"
 
 #define DEVICE_NAME "power"
-#define CLASS_NAME "power_class"
-#define BUFFER_SIZE 1024
-
-// 可以通过模块参数指定主设备号和次设备号
-static int power_major = 56; // 默认主设备号
-static int power_minor = 70;   // 默认次设备号
-module_param(power_major, int, S_IRUGO);
-module_param(power_minor, int, S_IRUGO);
-MODULE_PARM_DESC(power_major, "Major device number");
-MODULE_PARM_DESC(power_minor, "Minor device number");
-
-static struct class *char_class = NULL;
-static struct device *char_device = NULL;
-static struct cdev my_cdev;
-static dev_t dev_num;
-
-// 设备结构体
-struct power_dev
-{
-    char *buffer;
-    size_t size;
-    struct mutex lock;
+#define CLASS_NAME "power"
+#define BUFFER_SIZE 4096
+#define MAJOR_NUM 56
+#define MINOR_NUM 70
+#define TIMEOUT_LOOPS 100000
+
+static int major_num = MAJOR_NUM;
+static int minor_num = MINOR_NUM;
+static struct class *device_class = NULL;
+static struct device *device = NULL;
+
+/* 设备结构体 */
+struct chardev_device {
     struct cdev cdev;
-    wait_queue_head_t read_wait;
+    char *buffer;           /* 数据缓冲区 */
+    size_t data_len;        /* 当前数据长度 */
+    wait_queue_head_t read_queue;  /* 读等待队列 */
+    struct mutex lock;      /* 互斥锁 */
+    bool nonblock;          /* 非阻塞标志(可选) */
     struct delayed_work delay_work1;
+    char status;
 };
 
-static struct power_dev *dev = NULL;
+static struct chardev_device *chardev_dev;
 
-static void delay_work_func(struct work_struct *work)
-{    
-    printk(KERN_INFO "delay_work_func\n");
-}
 
-// 文件打开操作
-static int power_open(struct inode *inode, struct file *filp)
+static int wait_ibf(void)
 {
-    struct power_dev *dev;
-
-    dev = container_of(inode->i_cdev, struct power_dev, cdev);
-    filp->private_data = dev;
+    int i = 0;
+    while (inb(EC_CMD_PORT) & EC_IBF)
+    {
+        if (++i > TIMEOUT_LOOPS)
+        {
+            return -1;
+        }
+        udelay(1);
+    }
+    return 0;
+}
 
-    printk(KERN_INFO "power: Device opened (major=%d, minor=%d)\n",
-           imajor(inode), iminor(inode));
+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 power_release(struct inode *inode, struct file *filp)
+static int oem_ec_read_ram(uint8_t page, uint8_t offset, uint8_t *data)
 {
-    // release_region(PORT_80, 1);
-    printk(KERN_INFO "power: Device closed\n");
+    unsigned char WEC, REC;
+    switch(page)
+    {
+        case 0:
+        {
+            WEC = 0x96;
+            REC = 0x95;
+            break;
+        }
+        
+        case 1:
+        {
+            WEC = 0x98;
+            REC = 0x97;
+            break;
+        }
+        
+        default:
+        {
+            WEC = EC_VERSION_WEC;
+            REC = EC_VERSION_REC;
+            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;
 }
 
-// 读操作 - 支持cat命令
-static ssize_t power_read(struct file *filp, char __user *buf,
-                             size_t count, loff_t *f_pos)
+uint8_t ac_present(void) //read GPIO of DC IN 
 {
-    struct power_dev *dev = filp->private_data;
-    ssize_t retval = 0;
-    size_t available;
-    int read_count = 0;
-    int ret = 0;
+    uint8_t val = 0x00;
+    if (oem_ec_read_ram(2, 0x36, &val) < 0)
+        return 0;
 
-    if (mutex_lock_interruptible(&dev->lock))
-        return -ERESTARTSYS;
+    printk(" AC state is : %d\n", (val  & 0x03) ? 1 : 0);
+    return (val & 0x03) ? 1 : 0;
+}
 
-    // 这里read_count没有固定为4个字节,是为了便于后续扩展
-    if (count > dev->size)
-    {
-        read_count = dev->size;
-    }
-    else
-    {
-        read_count = count;
-    }
-    ret = copy_to_user(buf, dev->buffer, read_count);
-    if (ret != 0)
-    {
-        printk(KERN_INFO "power: copy_to_user failed\n");
-        goto out;
-    }
+uint8_t soft_rest_btn(void)//read GPIO of btn SW_HRST1 
+{
+    uint8_t val = 0x00;
+    if (oem_ec_read_ram(2, 0x34, &val) < 0)
+        return 0;
 
-    printk(KERN_INFO "power: Read %zu bytes\n", count);
+    printk(" btn state is : %d\n", (val & 0x01) ? 1 : 0);
+    return (val & 0x01) ? 1 : 0;
+}
 
-out:
-    mutex_unlock(&dev->lock);
-    return ret;
+/* 打开设备 */
+static int chardev_open(struct inode *inode, struct file *filp)
+{
+    struct chardev_device *dev = container_of(inode->i_cdev, 
+                                               struct chardev_device, cdev);
+    filp->private_data = dev;
+    dev_info(device, "Device opened\n");
+    return 0;
 }
 
-// 写操作
-static ssize_t power_write(struct file *filp, const char __user *buf,
-                              size_t count, loff_t *f_pos)
+/* 释放设备 */
+static int chardev_release(struct inode *inode, struct file *filp)
 {
-    struct power_dev *dev = filp->private_data;
-    ssize_t retval = 0;
-    size_t available;
-    int ret = 0;
+    dev_info(device, "Device released\n");
+    return 0;
+}
 
-    // 加锁
-    if (mutex_lock_interruptible(&dev->lock))
-    {
-        return -ERESTARTSYS;
+/* read 系统调用 */
+static ssize_t chardev_read(struct file *filp, char __user *buf, 
+                            size_t count, loff_t *f_pos)
+{
+    struct chardev_device *dev = filp->private_data;
+    ssize_t ret = 0;
+    size_t available;
+    
+    mutex_lock(&dev->lock);
+    
+    /* 等待数据可用(支持非阻塞和超时) */
+    while (dev->data_len == 0) {
+        if (filp->f_flags & O_NONBLOCK) {
+            mutex_unlock(&dev->lock);
+            return -EAGAIN;
+        }
+        
+        mutex_unlock(&dev->lock);
+        
+        /* 等待数据(可中断) */
+        if (wait_event_interruptible(dev->read_queue, dev->data_len > 0)) {
+            return -ERESTARTSYS;
+        }
+        
+        mutex_lock(&dev->lock);
     }
-
-    // 计算可写数据量
-    if (count > BUFFER_SIZE)
-    {
-        count = BUFFER_SIZE;
+    
+    /* 确定可读取的数据量 */
+    available = min(count, dev->data_len);
+    
+    /* 拷贝数据到用户空间 */
+    if (copy_to_user(buf, &chardev_dev->status, available)) {
+        mutex_unlock(&dev->lock);
+        return -EFAULT;
     }
-
-    // 拷贝数据
-    ret = copy_from_user(dev->buffer, buf, count);
-    if (ret != 0)
-    {
-        printk(KERN_INFO "power: copy_from_user failed\n");
-        goto out;
+    
+    /* 更新缓冲区(简单实现:读取后清空所有数据) */
+    if (available == dev->data_len) {
+        dev->data_len = 0;
+    } else {
+        /* 这里简单处理:移动剩余数据到开头 */
+        memmove(dev->buffer, dev->buffer + available, dev->data_len - available);
+        dev->data_len -= available;
     }
-
-    dev->size = count;
-
-    printk(KERN_INFO "power: Written %zu bytes\n", count);
-
-out:
+    
+    ret = available;
     mutex_unlock(&dev->lock);
+    
+    dev_dbg(device, "Read %zd bytes\n", ret);
+    return ret;
+}
 
-    // 调度延迟工作
-    if (ret == 0)
-    {
-        schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(10));
+/* poll 系统调用(支持 select) */
+static __poll_t chardev_poll(struct file *filp, struct poll_table_struct *wait)
+{
+    struct chardev_device *dev = filp->private_data;
+    __poll_t mask = 0;
+    
+    poll_wait(filp, &dev->read_queue, wait);
+    
+    mutex_lock(&dev->lock);
+    if (dev->data_len > 0) {
+        mask |= EPOLLIN | EPOLLRDNORM;  /* 数据可读 */
     }
-    return count;
+    mutex_unlock(&dev->lock);
+    
+    return mask;
 }
 
-static unsigned int power_poll(struct file *filp, poll_table *wait)
+/* 可选:提供写接口供测试用 */
+static ssize_t chardev_write(struct file *filp, const char __user *buf,
+                             size_t count, loff_t *f_pos)
 {
-    // poll_wait(filp, &dev->read_wait, wait);
-    return 0;
+    struct chardev_device *dev = filp->private_data;
+    ssize_t ret;
+    size_t to_write;
+    
+    mutex_lock(&dev->lock);
+    
+    to_write = min(count, BUFFER_SIZE - dev->data_len);
+    if (to_write == 0) {
+        mutex_unlock(&dev->lock);
+        return -ENOSPC;
+    }
+    
+    if (copy_from_user(dev->buffer + dev->data_len, buf, to_write)) {
+        mutex_unlock(&dev->lock);
+        return -EFAULT;
+    }
+    
+    dev->data_len += to_write;
+    ret = to_write;
+    
+    /* 唤醒等待读的进程 */
+    wake_up_interruptible(&dev->read_queue);
+    
+    mutex_unlock(&dev->lock);
+    
+    dev_dbg(device, "Written %zd bytes\n", ret);
+    return ret;
 }
 
-// 文件操作结构体
-static struct file_operations fops = {
+/* file_operations 结构 */
+static struct file_operations chardev_fops = {
     .owner = THIS_MODULE,
-    .open = power_open,
-    .release = power_release,
-    .read = power_read,
-    .write = power_write,
-    .poll = power_poll,
+    .open = chardev_open,
+    .release = chardev_release,
+    .read = chardev_read,
+    .write = chardev_write,
+    .poll = chardev_poll,
 };
 
-// 模块初始化
-int power_init(void)
+static void delay_work_func(struct work_struct *work)
 {
-    int result;
-
-    printk(KERN_INFO "power: Initializing driver with major=%d, minor=%d\n",
-           power_major, power_minor);
+    static int ac_flag = 0;
+    static int rst_flag = 0;
+    uint8_t ret = 0;
 
-    // 检查主设备号是否有效
-    if (power_major <= 0)
+    ret = ac_present();
+    if(ret == 1)
     {
-        printk(KERN_ALERT "power: Invalid major number %d\n", power_major);
-        return -EINVAL;
+        ac_flag++;
     }
-
-    // 构建设备号
-    dev_num = MKDEV(power_major, power_minor);
-
-    // 注册设备号 - 使用指定的主设备号
-    result = register_chrdev_region(dev_num, 1, DEVICE_NAME);
-    if (result < 0)
+    else
     {
-        printk(KERN_ALERT "power: Failed to register major number %d\n", power_major);
-        printk(KERN_ALERT "power: Try using a different major number\n");
-        return result;
+        ac_flag = 0;
     }
 
-    printk(KERN_INFO "power: Registered with major=%d, minor=%d\n",
-           MAJOR(dev_num), MINOR(dev_num));
-
-    // 分配设备结构体
-    dev = kmalloc(sizeof(struct power_dev), GFP_KERNEL);
-    if (!dev)
+    ret = soft_rest_btn();
+    if(ret == 1)
     {
-        result = -ENOMEM;
-        goto fail_malloc;
+        rst_flag++;
     }
-    memset(dev, 0, sizeof(struct power_dev));
-    init_waitqueue_head(&dev->read_wait);
-
-    // 分配缓冲区
-    dev->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
-    if (!dev->buffer)
+    else
     {
-        result = -ENOMEM;
-        goto fail_buffer;
+        rst_flag = 0;
     }
 
-    // 初始化互斥锁
-    mutex_init(&dev->lock);
-
-    INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
-
-    // 初始化字符设备
-    cdev_init(&dev->cdev, &fops);
-    dev->cdev.owner = THIS_MODULE;
-
-    // 添加字符设备到系统
-    result = cdev_add(&dev->cdev, dev_num, 1);
-    if (result)
+    if(ac_flag >= 16)
     {
-        printk(KERN_ALERT "power: Failed to add cdev\n");
-        goto fail_cdev;
+        chardev_dev->status = 'p';
+        chardev_dev->data_len = 1;
+        wake_up_interruptible(&chardev_dev->read_queue);
     }
-
-    // 创建设备类
-    char_class = class_create(THIS_MODULE, CLASS_NAME);
-    if (IS_ERR(char_class))
+    
+    if(rst_flag >= 3)
     {
-        result = PTR_ERR(char_class);
-        printk(KERN_ALERT "power: Failed to create class\n");
-        goto fail_class;
+        chardev_dev->status = 'r';
+        chardev_dev->data_len = 1;
+        wake_up_interruptible(&chardev_dev->read_queue);
     }
+    printk("ac_flag = %d, rst_flag = %d\n", ac_flag, rst_flag);
+	schedule_delayed_work(&chardev_dev->delay_work1, msecs_to_jiffies(500));
+}
 
-    // 创建设备
-    char_device = device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME);
-    if (IS_ERR(char_device))
-    {
-        result = PTR_ERR(char_device);
-        printk(KERN_ALERT "power: Failed to create device\n");
-        goto fail_device;
+/* 初始化设备 */
+int power_interface_init(void)
+{
+    int ret;
+    dev_t dev_num;
+    
+    /* 使用指定的主设备号和次设备号 */
+    dev_num = MKDEV(MAJOR_NUM, MINOR_NUM);
+    
+    /* 注册设备号(指定固定主设备号) */
+    ret = register_chrdev_region(dev_num, 1, DEVICE_NAME);
+    if (ret < 0) {
+        pr_err("Failed to register device number %d:%d\n", MAJOR_NUM, MINOR_NUM);
+        return ret;
+    }
+    major_num = MAJOR_NUM;
+    minor_num = MINOR_NUM;
+    pr_info("Registered device number %d:%d\n", major_num, minor_num);
+    
+    /* 分配设备结构体 */
+    chardev_dev = kzalloc(sizeof(struct chardev_device), GFP_KERNEL);
+    if (!chardev_dev) {
+        ret = -ENOMEM;
+        goto err_alloc_dev;
     }
 
-    printk(KERN_INFO "power: Driver initialized successfully\n");
-    printk(KERN_INFO "power: Device node: /dev/%s (major=%d, minor=%d)\n",
-           DEVICE_NAME, power_major, power_minor);
+    INIT_DELAYED_WORK(&chardev_dev->delay_work1, delay_work_func);
+    
+	schedule_delayed_work(&chardev_dev->delay_work1, msecs_to_jiffies(500));
+    
+    /* 分配数据缓冲区 */
+    chardev_dev->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
+    if (!chardev_dev->buffer) {
+        ret = -ENOMEM;
+        goto err_alloc_buffer;
+    }
+    
+    /* 初始化互斥锁和等待队列 */
+    mutex_init(&chardev_dev->lock);
+    init_waitqueue_head(&chardev_dev->read_queue);
+    chardev_dev->data_len = 0;
+    
+    /* 初始化 cdev */
+    cdev_init(&chardev_dev->cdev, &chardev_fops);
+    chardev_dev->cdev.owner = THIS_MODULE;
+    ret = cdev_add(&chardev_dev->cdev, dev_num, 1);
+    if (ret < 0) {
+        pr_err("Failed to add cdev\n");
+        goto err_cdev_add;
+    }
+    
+    /* 创建设备类 */
+    device_class = class_create(THIS_MODULE, CLASS_NAME);
+    if (IS_ERR(device_class)) {
+        ret = PTR_ERR(device_class);
+        pr_err("Failed to create class\n");
+        goto err_class_create;
+    }
+    
+    /* 创建设备节点 */
+    device = device_create(device_class, NULL, dev_num, NULL, DEVICE_NAME);
+    if (IS_ERR(device)) {
+        ret = PTR_ERR(device);
+        pr_err("Failed to create device\n");
+        goto err_device_create;
+    }
+    
+    pr_info("Char device demo loaded: major=%d, minor=%d\n", major_num, minor_num);
     return 0;
-
-fail_device:
-    class_destroy(char_class);
-fail_class:
-    cdev_del(&dev->cdev);
-fail_cdev:
-    kfree(dev->buffer);
-fail_buffer:
-    kfree(dev);
-fail_malloc:
+    
+err_device_create:
+    class_destroy(device_class);
+err_class_create:
+    cdev_del(&chardev_dev->cdev);
+err_cdev_add:
+    kfree(chardev_dev->buffer);
+err_alloc_buffer:
+    kfree(chardev_dev);
+err_alloc_dev:
     unregister_chrdev_region(dev_num, 1);
-    return result;
+    return ret;
 }
 
-// 模块退出
-void power_exit(void)
+/* 退出模块 */
+void power_interface_exit(void)
 {
-    cancel_delayed_work_sync(&dev->delay_work1);
-    device_destroy(char_class, dev_num);
-    class_destroy(char_class);
-
-    if (dev)
-    {
-        cdev_del(&dev->cdev);
-        if (dev->buffer)
-            kfree(dev->buffer);
-        kfree(dev);
-    }
-
+    dev_t dev_num = MKDEV(major_num, minor_num);
+    
+    cancel_delayed_work_sync(&chardev_dev->delay_work1);
+    
+    device_destroy(device_class, dev_num);
+    class_destroy(device_class);
+    cdev_del(&chardev_dev->cdev);
+    kfree(chardev_dev->buffer);
+    kfree(chardev_dev);
     unregister_chrdev_region(dev_num, 1);
-    printk(KERN_INFO "power: Driver removed (major=%d, minor=%d)\n",
-           power_major, power_minor);
+    
+    pr_info("Char device demo unloaded\n");
 }

+ 3 - 2
power.h

@@ -1,7 +1,8 @@
 #ifndef __POWER_INTERFACE_H__
 #define __POWER_INTERFACE_H__
 
-int power_init(void);
-void power_exit(void);
+
+int power_interface_init(void);
+void power_interface_exit(void);
 
 #endif

+ 2 - 1
test_app/build.sh

@@ -3,4 +3,5 @@
 gcc Beep_userspace.c -o Beep_userspace
 gcc setss.c -o setss
 gcc test_beep.c -o test_beep
-gcc test_ssegment.c -o test_ssegment
+gcc test_ssegment.c -o test_ssegment
+gcc power_app.c -o power_app

+ 74 - 0
test_app/power_app.c

@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <errno.h>
+
+#define DEVICE_PATH "/dev/power"
+#define BUFFER_SIZE 256
+
+int main() {
+    int fd;
+    fd_set read_fds;
+    struct timeval timeout;
+    char buffer[BUFFER_SIZE];
+    int ret;
+
+    // 1. 以非阻塞方式打开设备节点(也可以阻塞,但select会管理)
+    fd = open(DEVICE_PATH, O_RDONLY | O_NONBLOCK);
+    if (fd < 0) {
+        perror("open device failed");
+        return EXIT_FAILURE;
+    }
+
+    printf("Waiting for data from %s (select mode)...\n", DEVICE_PATH);
+
+    while (1) {
+        // 2. 清空文件描述符集,并加入我们的fd
+        FD_ZERO(&read_fds);
+        FD_SET(fd, &read_fds);
+
+        // 3. 设置超时时间(例如5秒)
+        timeout.tv_sec = 5;
+        timeout.tv_usec = 0;
+
+        // 4. 等待fd可读
+        ret = select(fd + 1, &read_fds, NULL, NULL, &timeout);
+
+        if (ret < 0) {
+            perror("select error");
+            close(fd);
+            return EXIT_FAILURE;
+        } else if (ret == 0) {
+            printf("select timeout after 5 seconds, continue waiting...\n");
+            continue;
+        }
+
+        // 5. 检查是否是我们的fd可读
+        if (FD_ISSET(fd, &read_fds)) {
+            memset(buffer, 0, BUFFER_SIZE);
+            ret = read(fd, buffer, BUFFER_SIZE - 1);
+            if (ret < 0) {
+                if (errno != EAGAIN) {
+                    perror("read error");
+                }
+                continue;
+            } else if (ret == 0) {
+                printf("device closed? ret=0\n");
+                break;
+            } else {
+                printf("Received %d bytes: %s\n", ret, buffer);
+                // 根据实际数据格式处理
+                // 例如解析电池电量等信息
+                if (strstr(buffer, "battery") != NULL) {
+                    printf("  -> Battery related data\n");
+                }
+            }
+        }
+    }
+
+    close(fd);
+    return EXIT_SUCCESS;
+}