Il Wed, Apr 18, 2007 at 01:50:29AM +0200, Luca Tettamanti ha scritto: 
> >I'm sure you've seen these:
> >  http://lists.lm-sensors.org/pipermail/lm-sensors/2005-October/014050.html
> >  http://www.lm-sensors.org/wiki/AsusFormulaHacking
> 
> Actually I haven't, I've happily ignored ACPI until now ;-) My DSDT
> doesn't look too bad, I may give it a try...

It wasn't hard :) Temperature reading works fine. AML code is still a
bit obscure though: the reading are enumerated in two different
sections:

* TSIF, FSIF, VSIF: they have read/write methods, easy to understand (I'm
  using these right now)

* SITM/GITM and GGPR for enumeration: more settings are available and in
  part they overlap the others. The methods are very strange, for e.g.
  temperature GITM (which calls GIT6) returns a hard-coded magic value;
  other methods (e.g. CPU frequency, Q-FAN settings) write a random
  number somewhere in the system memory and return an obscure magic
  value... very cool :)


I'm posting the proof-of-concept driver. A few notes:
- only temperature reading is implemented: I just got my MS degree so
  I've been drun^Wbusy
- sysfs files are still RO
- coding style: yes, I know... will cleanup
- There's a gazillion of debugging printk's :)
- I've extended the hwmon sysfs interface, with a new "temp%d_name";
  ACPI knows how the sensor is wired
- You'll need the following patch (against current Linus' git):

diff --git a/drivers/acpi/namespace/nsutils.c b/drivers/acpi/namespace/nsutils.c
index 90fd059..97e1139 100644
--- a/drivers/acpi/namespace/nsutils.c
+++ b/drivers/acpi/namespace/nsutils.c
@@ -700,6 +700,7 @@ struct acpi_namespace_node 
*acpi_ns_map_handle_to_node(acpi_handle handle)
 
        return (ACPI_CAST_PTR(struct acpi_namespace_node, handle));
 }
+EXPORT_SYMBOL(acpi_ns_map_handle_to_node);
 
 
/*******************************************************************************
  *
@@ -736,6 +737,7 @@ acpi_handle acpi_ns_convert_entry_to_handle(struct 
acpi_namespace_node *node)
        return ((acpi_handle) Node);
 ------------------------------------------------------*/
 }
+EXPORT_SYMBOL(acpi_ns_convert_entry_to_handle);
 
 
/*******************************************************************************
  *
@@ -875,6 +877,7 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
        ACPI_FREE(internal_path);
        return_ACPI_STATUS(status);
 }
+EXPORT_SYMBOL(acpi_ns_get_node);
 
 
/*******************************************************************************
  *

Now the driver:

/* 
 * Copyright (C) 2007 Luca Tettamanti <[EMAIL PROTECTED]>
 * Distribute under GPLv2.
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hwmon.h>

#include <acpi/acpi.h>
#include <acpi/acnamesp.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>


#define ATK_HID "ATK0110"
#define ATK_DRV "atk-hwmon"
#define ASOC "_SB.PCI0.SBRG.ASOC"

struct atk_data {
        struct class_device *class_dev; 
        acpi_handle atk_handle;
        struct acpi_device *device;

        acpi_handle rtmp_handle;
} atk_data;


typedef ssize_t (*sysfs_show_func)(struct device *dev, struct device_attribute 
*attr, char *buf);
typedef ssize_t (*sysfs_store_func)(struct device *dev, struct device_attribute 
*attr,
                        const char *buf, size_t count);


static void atk_init_attribute(struct device_attribute *attr, char *name, 
mode_t mode,
                sysfs_show_func show, sysfs_store_func store)
{
        attr->attr.name = name;
        attr->attr.mode = mode;
        attr->show = show;
        attr->store = store;
}

#define ATTR_NAME_SIZE 32

struct atk_temp_input {
        struct device_attribute dev_attr;
        /* id is used for the ACPI ID of the temp */
        int id;
        char attr_name[ATTR_NAME_SIZE];
};

struct atk_temp_input_list {
        int count;
        struct atk_temp_input temp_input[];
};

#define to_atk_temp_input(attr) \
        container_of(attr, struct atk_temp_input, dev_attr)

static ssize_t atk_temp_input_show(struct device *dev, struct device_attribute 
*attr, char *buf) {
        struct atk_temp_input *a = to_atk_temp_input(attr);
        union acpi_object temp;
        struct acpi_buffer ret;
        struct acpi_object_list params;
        union acpi_object id;
        acpi_status status;
        ssize_t count;

        id.type = ACPI_TYPE_INTEGER;
        id.integer.value = a->id;

        params.count = 1;
        params.pointer = &id;

        ret.length = sizeof(temp);
        ret.pointer = &temp;

        status = acpi_evaluate_object(atk_data.rtmp_handle, NULL, &params, 
&ret);
        if (status != AE_OK) {
                printk("acpi_evaluate_object: %s\n", 
acpi_format_exception(status));
                return -EIO;
        }

        if (temp.type != ACPI_TYPE_INTEGER) {
                printk("not an integer (%d)\n", temp.type);
                return -EIO;
        }

        /* ACPI returns centidegree */
        count = sprintf(buf, "%llu\n", temp.integer.value * 10);

        return count;
}

struct atk_temp_name {
        struct device_attribute dev_attr;
        char attr_name[ATTR_NAME_SIZE];
        char const *name;
};

struct atk_temp_name_list {
        int count;
        struct atk_temp_name temp_name[];
};

#define to_atk_temp_name(attr) \
        container_of(attr, struct atk_temp_name, dev_attr)

static ssize_t atk_temp_name_show(struct device *dev, struct device_attribute 
*attr, char *buf) {
        struct atk_temp_name *a = to_atk_temp_name(attr);

        return sprintf(buf, "%s\n", a->name);
}

enum atk_temp_pack_id {
        ATK_TEMP_PACK_MAX = 2,
        ATK_TEMP_PACK_CRIT = 3,
};

static int atk_temp_pack_read(acpi_handle handle, int pack_id, enum 
atk_temp_pack_id temp_id,
                unsigned long long *temp) {
        struct acpi_buffer ret;
        struct acpi_object_list params;
        union acpi_object id;
        union acpi_object *pack;
        union acpi_object *obj;
        acpi_status status;
        int err = 0;
        
        ret.length = ACPI_ALLOCATE_BUFFER;

        id.type = ACPI_TYPE_INTEGER;
        id.integer.value = pack_id;

        params.count = 1;
        params.pointer = &id;

        status = acpi_evaluate_object(handle, NULL, &params, &ret);
        if (status != AE_OK) {
                printk("atk_temp_pack_read: acpi_evaluate_object: %s\n",
                                acpi_format_exception(status));
                return -EIO;
        }

        pack = ret.pointer;

        if (pack->type != ACPI_TYPE_PACKAGE) {
                printk("atk_temp_pack_read: ret object is not a package: %d\n",
                        pack->type);

                err = -EIO;
                goto out;
        }
        if (pack->package.count != 5) {
                printk("atk_temp_pack_read: unexpected package size: %d\n",
                                pack->package.count);
                err = -EIO;
                goto out;
        }

        obj = &pack->package.elements[temp_id];
        if (obj->type != ACPI_TYPE_INTEGER) {
                printk("atk_temp_pack_read: unexepected type %d\n",
                                obj->type);
                err = -EIO;
                goto out;
        }

        *temp = obj->integer.value;
        
out:
        ACPI_FREE(ret.pointer);

        return err;
}

struct atk_temp_max {
        struct device_attribute dev_attr;
        int id;
        acpi_handle handle;
        char attr_name[ATTR_NAME_SIZE];
};

struct atk_temp_max_list {
        int count;
        struct atk_temp_max temp_max[];
};

#define to_atk_temp_max(attr) \
        container_of(attr, struct atk_temp_max, dev_attr)

static ssize_t atk_temp_max_show(struct device *dev, struct device_attribute 
*attr, char *buf) {
        struct atk_temp_max *a = to_atk_temp_max(attr);
        unsigned long long temp;

        if (atk_temp_pack_read(a->handle, a->id, ATK_TEMP_PACK_MAX, &temp))
                return -EIO;

        return sprintf(buf, "%lld\n", temp * 10);
}

struct atk_temp_crit {
        struct device_attribute dev_attr;
        int id;
        acpi_handle handle;
        char attr_name[ATTR_NAME_SIZE];
};

struct atk_temp_crit_list {
        int count;
        struct atk_temp_crit temp_crit[];
};

#define to_atk_temp_crit(attr) \
        container_of(attr, struct atk_temp_crit, dev_attr)

static ssize_t atk_temp_crit_show(struct device *dev, struct device_attribute 
*attr, char *buf) {
        struct atk_temp_crit *a = to_atk_temp_crit(attr);
        unsigned long long temp;

        if (atk_temp_pack_read(a->handle, a->id, ATK_TEMP_PACK_CRIT, &temp))
                return -EIO;

        return sprintf(buf, "%lld\n", temp * 10);
}

struct atk_temp_input_list *temp_input_list;
struct atk_temp_name_list *temp_name_list;
struct atk_temp_max_list *temp_max_list;
struct atk_temp_crit_list *temp_crit_list;

static int atk_found;

static int atk_add(struct acpi_device *device);
static int atk_remove(struct acpi_device *device, int type);

static struct acpi_driver atk_driver = {
        .name   = ATK_HID,
        .class  = "hwmon",
        .ids    = ATK_HID,
        .ops    = {
                .add    = atk_add,
                .remove = atk_remove,
        },
};

static int atk_create_files(struct device *dev) {
        int i;
        int ret;

        /* Temperatures */
        for (i = 0; i < temp_input_list->count; i++) {
                ret = device_create_file(dev, 
&temp_input_list->temp_input[i].dev_attr);
                if (ret)
                        return ret;
        }
        for (i = 0; i < temp_name_list->count; i++) {
                ret = device_create_file(dev, 
&temp_name_list->temp_name[i].dev_attr);
                if (ret)
                        return ret;
        }
        for (i = 0; i < temp_max_list->count; i++) {
                ret = device_create_file(dev, 
&temp_max_list->temp_max[i].dev_attr);
                if (ret)
                        return ret;
        }
        for (i = 0; i < temp_crit_list->count; i++) {
                ret = device_create_file(dev, 
&temp_crit_list->temp_crit[i].dev_attr);
                if (ret)
                        return ret;
        }

        return 0;
}

static void atk_remove_files(struct device *dev) {
        int i;

        /* Temperatures */
        if (temp_input_list) {
                for (i = 0; i < temp_input_list->count; i++)
                        device_remove_file(dev, 
&temp_input_list->temp_input[i].dev_attr);
        }
        kfree(temp_input_list);

        if (temp_name_list) {
                for (i = 0; i < temp_name_list->count; i++) {
                        device_remove_file(dev, 
&temp_name_list->temp_name[i].dev_attr);
                        kfree(temp_name_list->temp_name[i].name);
                }
        }
        kfree(temp_name_list);

        if (temp_max_list) {
                for (i = 0; i < temp_max_list->count; i++)
                        device_remove_file(dev, 
&temp_max_list->temp_max[i].dev_attr);
        }
        kfree(temp_max_list);

        if (temp_crit_list) {
                for (i = 0; i < temp_crit_list->count; i++)
                        device_remove_file(dev, 
&temp_crit_list->temp_crit[i].dev_attr);
        }
        kfree(temp_max_list);
}

static int atk_enumerate_temp(struct acpi_buffer *buf) {
        union acpi_object *pack;
        union acpi_object *obj;
        struct atk_temp_input_list *inputs;
        struct atk_temp_name_list *names;
        struct atk_temp_max_list *max;
        struct atk_temp_crit_list *crit;
        int i, ret;

        /* Result must be a package */
        pack = buf->pointer;
        if (pack->type != ACPI_TYPE_PACKAGE) {
                printk("atk_enumerate_temp: not a package\n");
                return -EINVAL;
        }

        if (pack->package.count < 1) {
                printk("atk_enumerate_temp: count < 1\n");
                return -EINVAL;
        }

        /* First field is the number of available readings */
        obj = &pack->package.elements[0];
        if (obj->type != ACPI_TYPE_INTEGER) {
                printk("atk_enumerate_temp: element 0 is not an int (%u)\n", 
obj->type);
                return -EINVAL;
        }

        /* Sanity check */
        if (pack->package.count != obj->integer.value + 1) {
                printk("atk_enumerate_temp: temperature count (%llu) differs 
from package count (%u)\n",
                                obj->integer.value, pack->package.count);
                return -EINVAL;
        }

        inputs = kzalloc(sizeof(struct atk_temp_input_list) + 
sizeof(*inputs->temp_input) * obj->integer.value, GFP_KERNEL);
        if (!inputs)
                return -ENOMEM;

        names = kzalloc(sizeof(struct atk_temp_name_list) + 
sizeof(*names->temp_name) * obj->integer.value, GFP_KERNEL);
        if (!names) {
                ret = -ENOMEM;
                goto cleanup4;
        }

        max = kzalloc(sizeof(*max) + sizeof(*max->temp_max) * 
obj->integer.value, GFP_KERNEL);
        if (!max) {
                ret = -ENOMEM;
                goto cleanup3;
        }

        crit = kzalloc(sizeof(*crit) + sizeof(*crit->temp_crit) * 
obj->integer.value, GFP_KERNEL);
        if (!crit) {
                ret = -ENOMEM;
                goto cleanup2;
        }

        inputs->count = names->count = max->count = crit->count = 
obj->integer.value;
        for (i = 0; i < pack->package.count - 1; i++) {
                struct acpi_buffer buf;
                union acpi_object *temp_pack;
                union acpi_object *name;
                union acpi_object *tmax;
                union acpi_object *tcrit;
                acpi_status status;

                obj = &pack->package.elements[i + 1];

                /* obj is a handle to the temperature package */
                if (obj->type != ACPI_TYPE_ANY) {
                        printk("atk_enumerate_temp: unexpected type: %d\n", 
obj->type);
                        ret = -EINVAL;
                        goto cleanup;
                }

                buf.length = ACPI_ALLOCATE_BUFFER;
                status = acpi_evaluate_object(obj->reference.handle, NULL, 
NULL, &buf);
                if (status != AE_OK) {
                        printk("ACPI exception on object %u: %s\n", i + 1, 
acpi_format_exception(status));
                        ret = -EINVAL;
                        goto cleanup;
                }

                /* Temperature package:
                 * byte buffer?
                 *   [3]: used by GITM/SITM to locate correct GITx/SITx,
                 *        method, same as the other package (GPID)
                 *   [2]: same as the other package, seems unused in
                 *        GITM/STIM
                 *   [1]: type?
                 *        1: cpu freq?
                 *        2: voltage
                 *        3: temperature
                 *        4: fan
                 *        7: Q-FAN (alarm?)
                 *        8: NOS
                 *   [0]: used by GITx/SITx for demux; selects the
                 *        value stored in ASB1 (PRM0)
                 * description
                 * max
                 * critical
                 * unknown
                 */
                temp_pack = buf.pointer;

                if (temp_pack->type != ACPI_TYPE_PACKAGE) {
                        printk("atk_enumerate_temp: unexpected type for package 
%u: %d\n", i + 1, temp_pack->type);
                        ret = -EINVAL;
                        goto cleanup;
                }

                if (temp_pack->package.count != 5) {
                        printk("Invalid package count: %u\n", 
temp_pack->package.count);
                        ret = -EINVAL;
                        goto cleanup;
                }

                name = &temp_pack->package.elements[1];
                tmax = &temp_pack->package.elements[2];
                tcrit = &temp_pack->package.elements[3];

                if (name->type != ACPI_TYPE_STRING) {
                        printk("atk_enumerate_temp: object %d not a string 
(%d)\n",
                                        i, name->type);
                        ret = -EINVAL;
                        goto cleanup;
                }

                if (tmax->type != ACPI_TYPE_INTEGER) {
                        printk("atk_enumerate_temp: object %d int expected 
(tmax), got: %d\n",
                                        i, tmax->type);
                        ret = -EINVAL;
                        goto cleanup;
                }
                if (tcrit->type != ACPI_TYPE_INTEGER) {
                        printk("atk_enumerate_temp: object %d int expected 
(tmax), got: %d\n",
                                        i, tcrit->type);
                        ret = -EINVAL;
                        goto cleanup;
                }

                inputs->temp_input[i].id = i;
                snprintf(inputs->temp_input[i].attr_name, ATTR_NAME_SIZE, 
"temp%d_input", i);
                atk_init_attribute(&inputs->temp_input[i].dev_attr, 
inputs->temp_input[i].attr_name,
                                0444, atk_temp_input_show, NULL);
                names->temp_name[i].name = kstrdup(name->string.pointer, 
GFP_KERNEL);
                
                snprintf(names->temp_name[i].attr_name, ATTR_NAME_SIZE, 
"temp%d_name", i);
                atk_init_attribute(&names->temp_name[i].dev_attr, 
names->temp_name[i].attr_name,
                                0444, atk_temp_name_show, NULL);
                
                max->temp_max[i].handle = obj->reference.handle;
                snprintf(max->temp_max[i].attr_name, ATTR_NAME_SIZE, 
"temp%d_max", i);
                atk_init_attribute(&max->temp_max[i].dev_attr, 
max->temp_max[i].attr_name,
                                0444, atk_temp_max_show, NULL);

                crit->temp_crit[i].handle = obj->reference.handle;
                snprintf(crit->temp_crit[i].attr_name, ATTR_NAME_SIZE, 
"temp%d_crit", i);
                atk_init_attribute(&crit->temp_crit[i].dev_attr, 
crit->temp_crit[i].attr_name,
                                0444, atk_temp_crit_show, NULL);

                printk("temp %u: %s [%llu-%llu]\n", inputs->temp_input[i].id, 
names->temp_name[i].name,
                                tmax->integer.value, tcrit->integer.value);

                ACPI_FREE(buf.pointer);
        }

        temp_name_list = names;
        temp_input_list = inputs;
        temp_max_list = max;
        temp_crit_list = crit;

        return 0;
cleanup:
        kfree(crit);
cleanup2:
        kfree(max);
cleanup3:
        for (i = 0; i < names->count; i++)
                kfree(names->temp_name[i].name);
        kfree(names);
cleanup4:
        kfree(inputs);

        return ret;
}

static int atk_add(struct acpi_device *device) {
        acpi_status ret;
        int err;
        struct acpi_buffer buf;
        union acpi_object *obj;
        struct acpi_namespace_node *search_ns;
        struct acpi_namespace_node *ns;

        printk("adding...\n");

        atk_data.device = device;
        atk_data.atk_handle = device->handle;
        acpi_driver_data(device) = &atk_data;

        buf.length = ACPI_ALLOCATE_BUFFER;
        ret = acpi_evaluate_object(atk_data.atk_handle, "MBIF", NULL, &buf);
        if (ret != AE_OK)
                return -ENODEV;

        obj = buf.pointer;
        if (obj->type == ACPI_TYPE_PACKAGE) {
                printk("id: %s\n", obj->package.elements[1].string.pointer);
        }
        ACPI_FREE(buf.pointer);

        /* Check for hwmon methods */
        search_ns = acpi_ns_map_handle_to_node(device->handle);
        if (search_ns == NULL) {
                printk("NULL\n");
                return -ENODEV;
        }

        /* RTMP: read temperature */
        ret = acpi_ns_get_node(search_ns, "RTMP", ACPI_NS_NO_UPSEARCH, &ns);
        if (ret != AE_OK) {
                printk(KERN_INFO "atk: RTMP not found\n");
                return -ENODEV;
        }

        atk_data.rtmp_handle = acpi_ns_convert_entry_to_handle(ns);

        /* Enumerate temp data - TSIF */
        buf.length = ACPI_ALLOCATE_BUFFER;
        ret = acpi_evaluate_object(atk_data.atk_handle, "TSIF", NULL, &buf);
        if (ret != AE_OK) {
                printk("TSIF failed: %s\n", acpi_format_exception(ret));
                return -ENODEV;
        }

        err = atk_enumerate_temp(&buf);
        ACPI_FREE(buf.pointer);
        if (err)
                return err;

        atk_found = 1;

        return 0;
}

static int atk_remove(struct acpi_device *device, int type) {
        printk("remove %d\n", type);

        acpi_driver_data(device) = NULL;

        return AE_OK;
}

int atk_init(void) {
        int ret;

        ret = acpi_bus_register_driver(&atk_driver);
        printk("%d\n", ret);
        if (ret)
                return ret;

        if (!atk_data.atk_handle) {
                ret = -ENODEV;
                goto bus_unreg;
        }

        if (!atk_found) {
                printk("atk not found\n");
                ret = -ENODEV;
                goto bus_unreg;
        }

        atk_data.class_dev = hwmon_device_register(&atk_data.device->dev);
        if (IS_ERR(atk_data.class_dev)) {
                ret = PTR_ERR(atk_data.class_dev);
                goto bus_unreg;
        }
        
        ret = atk_create_files(&atk_data.device->dev);
        if (ret)
                goto remove_files;

        return ret;

remove_files:
        atk_remove_files(&atk_data.device->dev);
        hwmon_device_unregister(atk_data.class_dev);
bus_unreg:
        acpi_bus_unregister_driver(&atk_driver);

        return ret;
}

void atk_exit(void) {
        hwmon_device_unregister(atk_data.class_dev);

        atk_remove_files(&atk_data.device->dev);

        acpi_bus_unregister_driver(&atk_driver);


}

module_init(atk_init);
module_exit(atk_exit);

MODULE_LICENSE("GPL");



Luca
-- 
Dicono che  il cane sia  il miglior  amico dell'uomo. Secondo me  non e`
vero. Quanti dei vostri amici avete fatto castrare, recentemente?
-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to