Selaa lähdekoodia

添加smart battery

monkeylqd 2 päivää sitten
vanhempi
sitoutus
fd311c28f7
4 muutettua tiedostoa jossa 482 lisäystä ja 1 poistoa
  1. 1 1
      Makefile
  2. 11 0
      main.c
  3. 463 0
      smart_battery.c
  4. 7 0
      smart_battery.h

+ 1 - 1
Makefile

@@ -3,7 +3,7 @@ CROSS_COMPILE=arm-poky-linux-gnueabi-
 
 obj-m := coral.o
 
-coral-objs := main.o led.o light_ring.o ssegment.o ec_version.o buzzer.o fan.o writeprotect.o myname.o cash_drawers.o batteryled.o watchdog.o power.o switches.o backlight.o gsensor.o lcd_2x20.o
+coral-objs := main.o led.o light_ring.o ssegment.o ec_version.o buzzer.o fan.o writeprotect.o myname.o cash_drawers.o batteryled.o watchdog.o power.o switches.o backlight.o gsensor.o lcd_2x20.o smart_battery.o
 
 
 KERNELDIR := /lib/modules/$(shell uname -r)/build

+ 11 - 0
main.c

@@ -32,6 +32,7 @@
 #include "backlight.h"
 #include "gsensor.h"
 #include "lcd_2x20.h"
+#include "smart_battery.h"
 
 struct kobject *vfiec_kobj = NULL;
 
@@ -143,8 +144,17 @@ static int __init all_driver_init(void)
         goto out_gensor;
     }
 
+    ret = battery_acpi_driver_init();
+    if(ret != 0)
+    {
+        printk(KERN_ERR "battery_acpi_driver_init failed\n");
+        goto out_lcd;
+    }
+
     printk(KERN_INFO "all_driver_init\n");
     return ret;
+out_battery_acpi:
+    battery_acpi_driver_exit();
 out_lcd:
     lcd2x20_exit();
 out_gensor:
@@ -200,6 +210,7 @@ static void __exit all_driver_exit(void)
     ssegment_exit();
     gsensor_exit_main();
     lcd2x20_exit();
+    battery_acpi_driver_exit();
     kobject_put(vfiec_kobj);
 }
 

+ 463 - 0
smart_battery.c

@@ -0,0 +1,463 @@
+#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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/power_supply.h>
+#include <linux/mutex.h>
+
+#define DEVICE_NAME "smart_battery"
+#define CLASS_NAME "battery_acpi_class"
+
+#define BATTERY_IOC_MAGIC 'B'
+#define BATTERY_IOC_GET_CAPACITY     _IOR(BATTERY_IOC_MAGIC, 1, int)
+#define BATTERY_IOC_GET_VOLTAGE      _IOR(BATTERY_IOC_MAGIC, 2, int)
+#define BATTERY_IOC_GET_STATUS       _IOR(BATTERY_IOC_MAGIC, 3, int)
+#define BATTERY_IOC_GET_ENERGY_NOW   _IOR(BATTERY_IOC_MAGIC, 4, int)
+#define BATTERY_IOC_GET_ENERGY_FULL  _IOR(BATTERY_IOC_MAGIC, 5, int)
+#define BATTERY_IOC_GET_POWER        _IOR(BATTERY_IOC_MAGIC, 6, int)
+#define BATTERY_IOC_GET_TECHNOLOGY   _IOR(BATTERY_IOC_MAGIC, 7, int)
+#define BATTERY_IOC_GET_SERIAL       _IOR(BATTERY_IOC_MAGIC, 8, char[16])
+#define BATTERY_IOC_GET_PRESENT      _IOR(BATTERY_IOC_MAGIC, 9, int)
+
+struct battery_acpi_device {
+    struct cdev cdev;
+    struct device *device;
+    struct class *class;
+    struct mutex lock;
+    char battery_name[16];
+    
+    /* 电池数据 */
+    int present;
+    int capacity;
+    int voltage;
+    int status;
+    int energy_now;
+    int energy_full;
+    int energy_full_design;
+    int power_now;
+    int technology;
+    char serial[16];
+    
+    unsigned int read_count;
+    unsigned int ioctl_count;
+};
+
+static struct battery_acpi_device *g_battery_dev;
+
+static const char* find_battery_name(void)
+{
+    static char name[16];
+    struct power_supply *psy;
+    int i;
+    
+    const char *battery_names[] = {"BAT4", "BAT0", "BAT1", "BAT2", "BAT3", "BATT"};
+    
+    for (i = 0; i < ARRAY_SIZE(battery_names); i++) {
+        psy = power_supply_get_by_name(battery_names[i]);
+        if (psy) {
+            if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY) {
+                strcpy(name, battery_names[i]);
+                power_supply_put(psy);
+                pr_info("Found battery: %s\n", name);
+                return name;
+            }
+            power_supply_put(psy);
+        }
+    }
+    
+    return NULL;
+}
+
+static int read_psy_property(const char *bat_name, enum power_supply_property prop, int *value)
+{
+    struct power_supply *psy;
+    union power_supply_propval val;
+    int ret;
+    
+    psy = power_supply_get_by_name(bat_name);
+    if (!psy)
+        return -ENODEV;
+    
+    ret = power_supply_get_property(psy, prop, &val);
+    if (ret == 0)
+        *value = val.intval;
+    
+    power_supply_put(psy);
+    return ret;
+}
+
+static int read_psy_property_string(const char *bat_name, enum power_supply_property prop, char *buf, size_t buf_size)
+{
+    struct power_supply *psy;
+    union power_supply_propval val;
+    int ret;
+    
+    psy = power_supply_get_by_name(bat_name);
+    if (!psy)
+        return -ENODEV;
+    
+    ret = power_supply_get_property(psy, prop, &val);
+    if (ret == 0 && val.strval) {
+        strncpy(buf, val.strval, buf_size - 1);
+        buf[buf_size - 1] = '\0';
+    } else {
+        buf[0] = '\0';
+    }
+    
+    power_supply_put(psy);
+    return ret;
+}
+
+static int charge_to_energy(int charge_uh, int voltage_uv)
+{
+    /* energy (µWh) = charge (µAh) * voltage (µV) / 1000000 */
+    if (charge_uh <= 0 || voltage_uv <= 0)
+        return 0;
+    return ((long long)charge_uh * voltage_uv) / 1000000;
+}
+
+static void update_battery_data(struct battery_acpi_device *dev)
+{
+    const char *bat_name;
+    int ret;
+    int charge_now = 0, charge_full = 0;
+    int energy_now_direct = 0, energy_full_direct = 0;
+    int voltage = 0;
+    
+    mutex_lock(&dev->lock);
+    
+    bat_name = find_battery_name();
+    if (!bat_name) {
+        dev->present = 0;
+        mutex_unlock(&dev->lock);
+        return;
+    }
+    
+    strcpy(dev->battery_name, bat_name);
+    
+    ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_PRESENT, &dev->present);
+    if (ret < 0)
+        dev->present = 1;
+    
+    if (!dev->present) {
+        mutex_unlock(&dev->lock);
+        return;
+    }
+    
+    read_psy_property(bat_name, POWER_SUPPLY_PROP_CAPACITY, &dev->capacity);
+    
+    read_psy_property(bat_name, POWER_SUPPLY_PROP_VOLTAGE_NOW, &dev->voltage);
+    voltage = dev->voltage;
+    
+    read_psy_property(bat_name, POWER_SUPPLY_PROP_STATUS, &dev->status);
+    
+
+    ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_ENERGY_NOW, &energy_now_direct);
+    if (ret == 0 && energy_now_direct > 0) {
+        dev->energy_now = energy_now_direct;
+        pr_debug("Using ENERGY_NOW: %d µWh\n", dev->energy_now);
+    } else {
+        ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_CHARGE_NOW, &charge_now);
+        if (ret == 0 && charge_now > 0 && voltage > 0) {
+            dev->energy_now = charge_to_energy(charge_now, voltage);
+            pr_debug("Calculated from CHARGE_NOW: %d µAh * %d µV = %d µWh\n", 
+                     charge_now, voltage, dev->energy_now);
+        } else {
+            dev->energy_now = -1;
+        }
+    }
+    
+    ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_ENERGY_FULL, &energy_full_direct);
+    if (ret == 0 && energy_full_direct > 0) {
+        dev->energy_full = energy_full_direct;
+        pr_debug("Using ENERGY_FULL: %d µWh\n", dev->energy_full);
+    } else {
+        ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_CHARGE_FULL, &charge_full);
+        if (ret == 0 && charge_full > 0 && voltage > 0) {
+            dev->energy_full = charge_to_energy(charge_full, voltage);
+            pr_debug("Calculated from CHARGE_FULL: %d µAh * %d µV = %d µWh\n", 
+                     charge_full, voltage, dev->energy_full);
+        } else {
+            ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &charge_full);
+            if (ret == 0 && charge_full > 0 && voltage > 0) {
+                dev->energy_full = charge_to_energy(charge_full, voltage);
+            } else {
+                dev->energy_full = -1;
+            }
+        }
+    }
+    
+    ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, &dev->energy_full_design);
+    if (ret < 0 || dev->energy_full_design <= 0) {
+        dev->energy_full_design = dev->energy_full;
+    }
+    
+    ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_POWER_NOW, &dev->power_now);
+    if (ret < 0) {
+        int current_now;
+        ret = read_psy_property(bat_name, POWER_SUPPLY_PROP_CURRENT_NOW, &current_now);
+        if (ret == 0 && voltage > 0) {
+            /* power (µW) = current (µA) * voltage (µV) / 1000000 */
+            dev->power_now = ((long long)current_now * voltage) / 1000000;
+        } else {
+            dev->power_now = 0;
+        }
+    }
+    
+    read_psy_property(bat_name, POWER_SUPPLY_PROP_TECHNOLOGY, &dev->technology);
+    
+    read_psy_property_string(bat_name, POWER_SUPPLY_PROP_SERIAL_NUMBER, dev->serial, sizeof(dev->serial));
+    if (dev->serial[0] == '\0')
+        strcpy(dev->serial, "N/A");
+    
+    mutex_unlock(&dev->lock);
+    
+    pr_debug("Battery %s: capacity=%d%%, voltage=%d µV, energy_now=%d µWh, energy_full=%d µWh\n",
+             dev->battery_name, dev->capacity, dev->voltage, dev->energy_now, dev->energy_full);
+}
+
+static const char* get_status_string(int status)
+{
+    switch (status) {
+    case POWER_SUPPLY_STATUS_CHARGING:
+        return "Charging";
+    case POWER_SUPPLY_STATUS_DISCHARGING:
+        return "Discharging";
+    case POWER_SUPPLY_STATUS_FULL:
+        return "Full";
+    case POWER_SUPPLY_STATUS_NOT_CHARGING:
+        return "Not charging";
+    default:
+        return "Unknown";
+    }
+}
+
+static const char* get_technology_string(int tech)
+{
+    switch (tech) {
+    case POWER_SUPPLY_TECHNOLOGY_LION:
+        return "Lithium-ion";
+    case POWER_SUPPLY_TECHNOLOGY_LIPO:
+        return "Lithium-polymer";
+    case POWER_SUPPLY_TECHNOLOGY_NiMH:
+        return "NiMH";
+    default:
+        return "Unknown";
+    }
+}
+
+static int battery_open(struct inode *inode, struct file *file)
+{
+    struct battery_acpi_device *dev = container_of(inode->i_cdev, 
+                                                    struct battery_acpi_device, 
+                                                    cdev);
+    file->private_data = dev;
+    update_battery_data(dev);
+    return 0;
+}
+
+static int battery_release(struct inode *inode, struct file *file)
+{
+    return 0;
+}
+
+static ssize_t battery_read(struct file *file, char __user *buf, 
+                           size_t count, loff_t *f_pos)
+{
+    struct battery_acpi_device *dev = file->private_data;
+    char info[512];
+    int len;
+    
+    if (*f_pos > 0)
+        return 0;
+    
+    update_battery_data(dev);
+    dev->read_count++;
+    
+    if (!dev->present) {
+        len = snprintf(info, sizeof(info),
+                       "=== Battery Information ===\n"
+                       "  Battery: NOT PRESENT\n"
+                       "  Read Count: %u\n",
+                       dev->read_count);
+    } else {
+        len = snprintf(info, sizeof(info),
+                       "=== Battery Information ===\n"
+                       "  Battery Name:      %s\n"
+                       "  Present:           Yes\n"
+                       "  Capacity:          %d%%\n"
+                       "  Voltage:           %d.%03d V\n"
+                       "  Status:            %s\n"
+                       "  Energy Now:        %d.%02d Wh\n"
+                       "  Energy Full:       %d.%02d Wh\n"
+                       "  Energy Full Design:%d.%02d Wh\n"
+                       "  Power Now:         %d.%02d W\n"
+                       "  Technology:        %s\n"
+                       "  Serial Number:     %s\n"
+                       "  Read Count:        %u\n",
+                       dev->battery_name,
+                       dev->capacity,
+                       dev->voltage / 1000000,
+                       (dev->voltage % 1000000) / 1000,
+                       get_status_string(dev->status),
+                       dev->energy_now * (dev->voltage / 1000000) / 1000000,
+                       ((dev->energy_now * (dev->voltage / 1000000)) % 1000000) / 10000,
+                       dev->energy_full * (dev->voltage / 1000000) / 1000000,
+                       ((dev->energy_full * (dev->voltage / 1000000)) % 1000000) / 10000,
+                       dev->energy_full_design * (dev->voltage / 1000000) / 1000000,
+                       ((dev->energy_full_design * (dev->voltage / 1000000)) % 1000000) / 10000,
+                       dev->power_now / 1000000,
+                       (dev->power_now % 1000000) / 10000,
+                       get_technology_string(dev->technology),
+                       dev->serial,
+                       dev->read_count);
+    }
+    
+    if (len > count)
+        len = count;
+    
+    if (copy_to_user(buf, info, len))
+        return -EFAULT;
+    
+    *f_pos += len;
+    return len;
+}
+
+static long battery_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+    struct battery_acpi_device *dev = file->private_data;
+    void __user *argp = (void __user *)arg;
+    
+    update_battery_data(dev);
+    dev->ioctl_count++;
+    
+    switch (cmd) {
+    case BATTERY_IOC_GET_PRESENT:
+        if (copy_to_user(argp, &dev->present, sizeof(dev->present)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_CAPACITY:
+        if (copy_to_user(argp, &dev->capacity, sizeof(dev->capacity)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_VOLTAGE:
+        if (copy_to_user(argp, &dev->voltage, sizeof(dev->voltage)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_STATUS:
+        if (copy_to_user(argp, &dev->status, sizeof(dev->status)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_ENERGY_NOW:
+        if (copy_to_user(argp, &dev->energy_now, sizeof(dev->energy_now)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_ENERGY_FULL:
+        if (copy_to_user(argp, &dev->energy_full, sizeof(dev->energy_full)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_POWER:
+        if (copy_to_user(argp, &dev->power_now, sizeof(dev->power_now)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_TECHNOLOGY:
+        if (copy_to_user(argp, &dev->technology, sizeof(dev->technology)))
+            return -EFAULT;
+        break;
+    case BATTERY_IOC_GET_SERIAL:
+        if (copy_to_user(argp, dev->serial, sizeof(dev->serial)))
+            return -EFAULT;
+        break;
+    default:
+        return -ENOTTY;
+    }
+    
+    return 0;
+}
+
+static const struct file_operations battery_fops = {
+    .owner = THIS_MODULE,
+    .open = battery_open,
+    .release = battery_release,
+    .read = battery_read,
+    .unlocked_ioctl = battery_ioctl,
+};
+
+int battery_acpi_driver_init(void)
+{
+    dev_t dev_num;
+    int ret;
+    struct battery_acpi_device *dev;
+    
+    pr_info("Initializing Battery Character Device Driver v2.2\n");
+    
+    ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
+    if (ret < 0) {
+        pr_err("Failed to allocate device number\n");
+        return ret;
+    }
+    
+    dev = kzalloc(sizeof(struct battery_acpi_device), GFP_KERNEL);
+    if (!dev) {
+        ret = -ENOMEM;
+        goto err_alloc;
+    }
+    
+    mutex_init(&dev->lock);
+    
+    dev->class = class_create(THIS_MODULE, CLASS_NAME);
+    if (IS_ERR(dev->class)) {
+        ret = PTR_ERR(dev->class);
+        goto err_class;
+    }
+    
+    dev->device = device_create(dev->class, NULL, dev_num, dev, DEVICE_NAME);
+    if (IS_ERR(dev->device)) {
+        ret = PTR_ERR(dev->device);
+        goto err_device;
+    }
+    
+    cdev_init(&dev->cdev, &battery_fops);
+    dev->cdev.owner = THIS_MODULE;
+    ret = cdev_add(&dev->cdev, dev_num, 1);
+    if (ret < 0) {
+        goto err_cdev;
+    }
+    
+    g_battery_dev = dev;
+    update_battery_data(dev);
+    
+    pr_info("Battery driver initialized: /dev/%s\n", DEVICE_NAME);
+    
+    return 0;
+    
+err_cdev:
+    device_destroy(dev->class, dev_num);
+err_device:
+    class_destroy(dev->class);
+err_class:
+    mutex_destroy(&dev->lock);
+    kfree(dev);
+err_alloc:
+    unregister_chrdev_region(dev_num, 1);
+    return ret;
+}
+
+void battery_acpi_driver_exit(void)
+{
+    dev_t dev_num = g_battery_dev->cdev.dev;
+    
+    cdev_del(&g_battery_dev->cdev);
+    device_destroy(g_battery_dev->class, dev_num);
+    class_destroy(g_battery_dev->class);
+    mutex_destroy(&g_battery_dev->lock);
+    kfree(g_battery_dev);
+    unregister_chrdev_region(dev_num, 1);
+    
+    pr_info("Battery driver exited\n");
+}
+

+ 7 - 0
smart_battery.h

@@ -0,0 +1,7 @@
+#ifndef __SMART_BATTERY_H__
+#define __SMART_BATTERY_H__
+
+int battery_acpi_driver_init(void);
+void battery_acpi_driver_exit(void);
+
+#endif