linux/drivers/acpi/fan.c
<<
>>
Prefs
   1/*
   2 *  acpi_fan.c - ACPI Fan Driver ($Revision: 29 $)
   3 *
   4 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
   5 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
   6 *
   7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; either version 2 of the License, or (at
  12 *  your option) any later version.
  13 *
  14 *  This program is distributed in the hope that it will be useful, but
  15 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 *  General Public License for more details.
  18 *
  19 *  You should have received a copy of the GNU General Public License along
  20 *  with this program; if not, write to the Free Software Foundation, Inc.,
  21 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  22 *
  23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/module.h>
  28#include <linux/init.h>
  29#include <linux/types.h>
  30#include <linux/proc_fs.h>
  31#include <linux/seq_file.h>
  32#include <asm/uaccess.h>
  33#include <linux/thermal.h>
  34#include <acpi/acpi_bus.h>
  35#include <acpi/acpi_drivers.h>
  36
  37#define ACPI_FAN_CLASS                  "fan"
  38#define ACPI_FAN_FILE_STATE             "state"
  39
  40#define _COMPONENT              ACPI_FAN_COMPONENT
  41ACPI_MODULE_NAME("fan");
  42
  43MODULE_AUTHOR("Paul Diefenbaugh");
  44MODULE_DESCRIPTION("ACPI Fan Driver");
  45MODULE_LICENSE("GPL");
  46
  47static int acpi_fan_add(struct acpi_device *device);
  48static int acpi_fan_remove(struct acpi_device *device, int type);
  49static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state);
  50static int acpi_fan_resume(struct acpi_device *device);
  51
  52static const struct acpi_device_id fan_device_ids[] = {
  53        {"PNP0C0B", 0},
  54        {"", 0},
  55};
  56MODULE_DEVICE_TABLE(acpi, fan_device_ids);
  57
  58static struct acpi_driver acpi_fan_driver = {
  59        .name = "fan",
  60        .class = ACPI_FAN_CLASS,
  61        .ids = fan_device_ids,
  62        .ops = {
  63                .add = acpi_fan_add,
  64                .remove = acpi_fan_remove,
  65                .suspend = acpi_fan_suspend,
  66                .resume = acpi_fan_resume,
  67                },
  68};
  69
  70/* thermal cooling device callbacks */
  71static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
  72                             *state)
  73{
  74        /* ACPI fan device only support two states: ON/OFF */
  75        *state = 1;
  76        return 0;
  77}
  78
  79static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
  80                             *state)
  81{
  82        struct acpi_device *device = cdev->devdata;
  83        int result;
  84        int acpi_state;
  85
  86        if (!device)
  87                return -EINVAL;
  88
  89        result = acpi_bus_get_power(device->handle, &acpi_state);
  90        if (result)
  91                return result;
  92
  93        *state = (acpi_state == ACPI_STATE_D3 ? 0 :
  94                 (acpi_state == ACPI_STATE_D0 ? 1 : -1));
  95        return 0;
  96}
  97
  98static int
  99fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 100{
 101        struct acpi_device *device = cdev->devdata;
 102        int result;
 103
 104        if (!device || (state != 0 && state != 1))
 105                return -EINVAL;
 106
 107        result = acpi_bus_set_power(device->handle,
 108                                state ? ACPI_STATE_D0 : ACPI_STATE_D3);
 109
 110        return result;
 111}
 112
 113static struct thermal_cooling_device_ops fan_cooling_ops = {
 114        .get_max_state = fan_get_max_state,
 115        .get_cur_state = fan_get_cur_state,
 116        .set_cur_state = fan_set_cur_state,
 117};
 118
 119/* --------------------------------------------------------------------------
 120                              FS Interface (/proc)
 121   -------------------------------------------------------------------------- */
 122#ifdef CONFIG_ACPI_PROCFS
 123
 124static struct proc_dir_entry *acpi_fan_dir;
 125
 126static int acpi_fan_read_state(struct seq_file *seq, void *offset)
 127{
 128        struct acpi_device *device = seq->private;
 129        int state = 0;
 130
 131
 132        if (device) {
 133                if (acpi_bus_get_power(device->handle, &state))
 134                        seq_printf(seq, "status:                  ERROR\n");
 135                else
 136                        seq_printf(seq, "status:                  %s\n",
 137                                   !state ? "on" : "off");
 138        }
 139        return 0;
 140}
 141
 142static int acpi_fan_state_open_fs(struct inode *inode, struct file *file)
 143{
 144        return single_open(file, acpi_fan_read_state, PDE(inode)->data);
 145}
 146
 147static ssize_t
 148acpi_fan_write_state(struct file *file, const char __user * buffer,
 149                     size_t count, loff_t * ppos)
 150{
 151        int result = 0;
 152        struct seq_file *m = file->private_data;
 153        struct acpi_device *device = m->private;
 154        char state_string[3] = { '\0' };
 155
 156        if (count > sizeof(state_string) - 1)
 157                return -EINVAL;
 158
 159        if (copy_from_user(state_string, buffer, count))
 160                return -EFAULT;
 161
 162        state_string[count] = '\0';
 163        if ((state_string[0] < '0') || (state_string[0] > '3'))
 164                return -EINVAL;
 165        if (state_string[1] == '\n')
 166                state_string[1] = '\0';
 167        if (state_string[1] != '\0')
 168                return -EINVAL;
 169
 170        result = acpi_bus_set_power(device->handle,
 171                                    simple_strtoul(state_string, NULL, 0));
 172        if (result)
 173                return result;
 174
 175        return count;
 176}
 177
 178static const struct file_operations acpi_fan_state_ops = {
 179        .open = acpi_fan_state_open_fs,
 180        .read = seq_read,
 181        .write = acpi_fan_write_state,
 182        .llseek = seq_lseek,
 183        .release = single_release,
 184        .owner = THIS_MODULE,
 185};
 186
 187static int acpi_fan_add_fs(struct acpi_device *device)
 188{
 189        struct proc_dir_entry *entry = NULL;
 190
 191
 192        if (!device)
 193                return -EINVAL;
 194
 195        if (!acpi_device_dir(device)) {
 196                acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
 197                                                     acpi_fan_dir);
 198                if (!acpi_device_dir(device))
 199                        return -ENODEV;
 200        }
 201
 202        /* 'status' [R/W] */
 203        entry = proc_create_data(ACPI_FAN_FILE_STATE,
 204                                 S_IFREG | S_IRUGO | S_IWUSR,
 205                                 acpi_device_dir(device),
 206                                 &acpi_fan_state_ops,
 207                                 device);
 208        if (!entry)
 209                return -ENODEV;
 210        return 0;
 211}
 212
 213static int acpi_fan_remove_fs(struct acpi_device *device)
 214{
 215
 216        if (acpi_device_dir(device)) {
 217                remove_proc_entry(ACPI_FAN_FILE_STATE, acpi_device_dir(device));
 218                remove_proc_entry(acpi_device_bid(device), acpi_fan_dir);
 219                acpi_device_dir(device) = NULL;
 220        }
 221
 222        return 0;
 223}
 224#else
 225static int acpi_fan_add_fs(struct acpi_device *device)
 226{
 227        return 0;
 228}
 229
 230static int acpi_fan_remove_fs(struct acpi_device *device)
 231{
 232        return 0;
 233}
 234#endif
 235/* --------------------------------------------------------------------------
 236                                 Driver Interface
 237   -------------------------------------------------------------------------- */
 238
 239static int acpi_fan_add(struct acpi_device *device)
 240{
 241        int result = 0;
 242        int state = 0;
 243        struct thermal_cooling_device *cdev;
 244
 245        if (!device)
 246                return -EINVAL;
 247
 248        strcpy(acpi_device_name(device), "Fan");
 249        strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
 250
 251        result = acpi_bus_get_power(device->handle, &state);
 252        if (result) {
 253                printk(KERN_ERR PREFIX "Reading power state\n");
 254                goto end;
 255        }
 256
 257        device->flags.force_power_state = 1;
 258        acpi_bus_set_power(device->handle, state);
 259        device->flags.force_power_state = 0;
 260
 261        cdev = thermal_cooling_device_register("Fan", device,
 262                                                &fan_cooling_ops);
 263        if (IS_ERR(cdev)) {
 264                result = PTR_ERR(cdev);
 265                goto end;
 266        }
 267
 268        dev_info(&device->dev, "registered as cooling_device%d\n", cdev->id);
 269
 270        device->driver_data = cdev;
 271        result = sysfs_create_link(&device->dev.kobj,
 272                                   &cdev->device.kobj,
 273                                   "thermal_cooling");
 274        if (result)
 275                dev_err(&device->dev, "Failed to create sysfs link "
 276                        "'thermal_cooling'\n");
 277
 278        result = sysfs_create_link(&cdev->device.kobj,
 279                                   &device->dev.kobj,
 280                                   "device");
 281        if (result)
 282                dev_err(&device->dev, "Failed to create sysfs link "
 283                        "'device'\n");
 284
 285        result = acpi_fan_add_fs(device);
 286        if (result)
 287                goto end;
 288
 289        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 290               acpi_device_name(device), acpi_device_bid(device),
 291               !device->power.state ? "on" : "off");
 292
 293      end:
 294        return result;
 295}
 296
 297static int acpi_fan_remove(struct acpi_device *device, int type)
 298{
 299        struct thermal_cooling_device *cdev = acpi_driver_data(device);
 300
 301        if (!device || !cdev)
 302                return -EINVAL;
 303
 304        acpi_fan_remove_fs(device);
 305        sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 306        sysfs_remove_link(&cdev->device.kobj, "device");
 307        thermal_cooling_device_unregister(cdev);
 308
 309        return 0;
 310}
 311
 312static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state)
 313{
 314        if (!device)
 315                return -EINVAL;
 316
 317        acpi_bus_set_power(device->handle, ACPI_STATE_D0);
 318
 319        return AE_OK;
 320}
 321
 322static int acpi_fan_resume(struct acpi_device *device)
 323{
 324        int result = 0;
 325        int power_state = 0;
 326
 327        if (!device)
 328                return -EINVAL;
 329
 330        result = acpi_bus_get_power(device->handle, &power_state);
 331        if (result) {
 332                printk(KERN_ERR PREFIX
 333                                  "Error reading fan power state\n");
 334                return result;
 335        }
 336
 337        device->flags.force_power_state = 1;
 338        acpi_bus_set_power(device->handle, power_state);
 339        device->flags.force_power_state = 0;
 340
 341        return result;
 342}
 343
 344static int __init acpi_fan_init(void)
 345{
 346        int result = 0;
 347
 348
 349#ifdef CONFIG_ACPI_PROCFS
 350        acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
 351        if (!acpi_fan_dir)
 352                return -ENODEV;
 353#endif
 354
 355        result = acpi_bus_register_driver(&acpi_fan_driver);
 356        if (result < 0) {
 357                remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
 358                return -ENODEV;
 359        }
 360
 361        return 0;
 362}
 363
 364static void __exit acpi_fan_exit(void)
 365{
 366
 367        acpi_bus_unregister_driver(&acpi_fan_driver);
 368
 369        remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
 370
 371        return;
 372}
 373
 374module_init(acpi_fan_init);
 375module_exit(acpi_fan_exit);
 376