Skip to content

Commit

Permalink
hwmon: (asus_atk0110) Add debugfs interface
Browse files Browse the repository at this point in the history
Expose the raw GGRP/GITM interface via debugfs. The hwmon interface is
reverse engineered and the driver tends to break on newer boards...
Using this interface it's possible to poke directly at the ACPI methods
without the need to recompile, reducing the guesswork and the round trips
needed to support a new revision of the interface.

Signed-off-by: Luca Tettamanti <kronos.it@gmail.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
  • Loading branch information
Luca Tettamanti authored and Jean Delvare committed Jan 10, 2010
1 parent 8ba406b commit 7e5eab1
Showing 1 changed file with 191 additions and 0 deletions.
191 changes: 191 additions & 0 deletions drivers/hwmon/asus_atk0110.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* See COPYING in the top level directory of the kernel tree.
*/

#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/hwmon.h>
#include <linux/list.h>
Expand Down Expand Up @@ -101,6 +102,11 @@ struct atk_data {
int temperature_count;
int fan_count;
struct list_head sensor_list;

struct {
struct dentry *root;
u32 id;
} debugfs;
};


Expand Down Expand Up @@ -624,6 +630,187 @@ static int atk_read_value(struct atk_sensor_data *sensor, u64 *value)
return err;
}

#ifdef CONFIG_DEBUG_FS
static int atk_debugfs_gitm_get(void *p, u64 *val)
{
struct atk_data *data = p;
union acpi_object *ret;
struct atk_acpi_ret_buffer *buf;
int err = 0;

if (!data->read_handle)
return -ENODEV;

if (!data->debugfs.id)
return -EINVAL;

ret = atk_gitm(data, data->debugfs.id);
if (IS_ERR(ret))
return PTR_ERR(ret);

buf = (struct atk_acpi_ret_buffer *)ret->buffer.pointer;
if (buf->flags)
*val = buf->value;
else
err = -EIO;

return err;
}

DEFINE_SIMPLE_ATTRIBUTE(atk_debugfs_gitm,
atk_debugfs_gitm_get,
NULL,
"0x%08llx\n")

static int atk_acpi_print(char *buf, size_t sz, union acpi_object *obj)
{
int ret = 0;

switch (obj->type) {
case ACPI_TYPE_INTEGER:
ret = snprintf(buf, sz, "0x%08llx\n", obj->integer.value);
break;
case ACPI_TYPE_STRING:
ret = snprintf(buf, sz, "%s\n", obj->string.pointer);
break;
}

return ret;
}

static void atk_pack_print(char *buf, size_t sz, union acpi_object *pack)
{
int ret;
int i;

for (i = 0; i < pack->package.count; i++) {
union acpi_object *obj = &pack->package.elements[i];

ret = atk_acpi_print(buf, sz, obj);
if (ret >= sz)
break;
buf += ret;
sz -= ret;
}
}

static int atk_debugfs_ggrp_open(struct inode *inode, struct file *file)
{
struct atk_data *data = inode->i_private;
char *buf = NULL;
union acpi_object *ret;
u8 cls;
int i;

if (!data->enumerate_handle)
return -ENODEV;
if (!data->debugfs.id)
return -EINVAL;

cls = (data->debugfs.id & 0xff000000) >> 24;
ret = atk_ggrp(data, cls);
if (IS_ERR(ret))
return PTR_ERR(ret);

for (i = 0; i < ret->package.count; i++) {
union acpi_object *pack = &ret->package.elements[i];
union acpi_object *id;

if (pack->type != ACPI_TYPE_PACKAGE)
continue;
if (!pack->package.count)
continue;
id = &pack->package.elements[0];
if (id->integer.value == data->debugfs.id) {
/* Print the package */
buf = kzalloc(512, GFP_KERNEL);
if (!buf) {
ACPI_FREE(ret);
return -ENOMEM;
}
atk_pack_print(buf, 512, pack);
break;
}
}
ACPI_FREE(ret);

if (!buf)
return -EINVAL;

file->private_data = buf;

return nonseekable_open(inode, file);
}

static ssize_t atk_debugfs_ggrp_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
char *str = file->private_data;
size_t len = strlen(str);

return simple_read_from_buffer(buf, count, pos, str, len);
}

static int atk_debugfs_ggrp_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}

static const struct file_operations atk_debugfs_ggrp_fops = {
.read = atk_debugfs_ggrp_read,
.open = atk_debugfs_ggrp_open,
.release = atk_debugfs_ggrp_release,
};

static void atk_debugfs_init(struct atk_data *data)
{
struct dentry *d;
struct dentry *f;

data->debugfs.id = 0;

d = debugfs_create_dir("asus_atk0110", NULL);
if (!d || IS_ERR(d))
return;

f = debugfs_create_x32("id", S_IRUSR | S_IWUSR, d, &data->debugfs.id);
if (!f || IS_ERR(f))
goto cleanup;

f = debugfs_create_file("gitm", S_IRUSR, d, data,
&atk_debugfs_gitm);
if (!f || IS_ERR(f))
goto cleanup;

f = debugfs_create_file("ggrp", S_IRUSR, d, data,
&atk_debugfs_ggrp_fops);
if (!f || IS_ERR(f))
goto cleanup;

data->debugfs.root = d;

return;
cleanup:
debugfs_remove_recursive(d);
}

static void atk_debugfs_cleanup(struct atk_data *data)
{
debugfs_remove_recursive(data->debugfs.root);
}

#else /* CONFIG_DEBUG_FS */

static void atk_debugfs_init(struct atk_data *data)
{
}

static void atk_debugfs_cleanup(struct atk_data *data)
{
}
#endif

static int atk_add_sensor(struct atk_data *data, union acpi_object *obj)
{
struct device *dev = &data->acpi_dev->dev;
Expand Down Expand Up @@ -1180,6 +1367,8 @@ static int atk_add(struct acpi_device *device)
if (err)
goto cleanup;

atk_debugfs_init(data);

device->driver_data = data;
return 0;
cleanup:
Expand All @@ -1198,6 +1387,8 @@ static int atk_remove(struct acpi_device *device, int type)

device->driver_data = NULL;

atk_debugfs_cleanup(data);

atk_remove_files(data);
atk_free_sensors(data);
hwmon_device_unregister(data->hwmon_dev);
Expand Down

0 comments on commit 7e5eab1

Please sign in to comment.