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 PREFIX "ACPI: "
  38
  39#define ACPI_FAN_CLASS                  "fan"
  40#define ACPI_FAN_FILE_STATE             "state"
  41
  42#define _COMPONENT              ACPI_FAN_COMPONENT
  43ACPI_MODULE_NAME("fan");
  44
  45MODULE_AUTHOR("Paul Diefenbaugh");
  46MODULE_DESCRIPTION("ACPI Fan Driver");
  47MODULE_LICENSE("GPL");
  48
  49static int acpi_fan_add(struct acpi_device *device);
  50static int acpi_fan_remove(struct acpi_device *device, int type);
  51static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state);
  52static int acpi_fan_resume(struct acpi_device *device);
  53
  54static const struct acpi_device_id fan_device_ids[] = {
  55        {"PNP0C0B", 0},
  56        {"", 0},
  57};
  58MODULE_DEVICE_TABLE(acpi, fan_device_ids);
  59
  60static struct acpi_driver acpi_fan_driver = {
  61        .name = "fan",
  62        .class = ACPI_FAN_CLASS,
  63        .ids = fan_device_ids,
  64        .ops = {
  65                .add = acpi_fan_add,
  66                .remove = acpi_fan_remove,
  67                .suspend = acpi_fan_suspend,
  68                .resume = acpi_fan_resume,
  69                },
  70};
  71
  72/* thermal cooling device callbacks */
  73static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
  74                             *state)
  75{
  76        /* ACPI fan device only support two states: ON/OFF */
  77        *state = 1;
  78        return 0;
  79}
  80
  81static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
  82                             *state)
  83{
  84        struct acpi_device *device = cdev->devdata;
  85        int result;
  86        int acpi_state;
  87
  88        if (!device)
  89                return -EINVAL;
  90
  91        result = acpi_bus_get_power(device->handle, &acpi_state);
  92        if (result)
  93                return result;
  94
  95        *state = (acpi_state == ACPI_STATE_D3 ? 0 :
  96                 (acpi_state == ACPI_STATE_D0 ? 1 : -1));
  97        return 0;
  98}
  99
 100static int
 101fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 102{
 103        struct acpi_device *device = cdev->devdata;
 104        int result;
 105
 106        if (!device || (state != 0 && state != 1))
 107                return -EINVAL;
 108
 109        result = acpi_bus_set_power(device->handle,
 110                                state ? ACPI_STATE_D0 : ACPI_STATE_D3);
 111
 112        return result;
 113}
 114
 115static struct thermal_cooling_device_ops fan_cooling_ops = {
 116        .get_max_state = fan_get_max_state,
 117        .get_cur_state = fan_get_cur_state,
 118        .set_cur_state = fan_set_cur_state,
 119};
 120
 121/* --------------------------------------------------------------------------
 122                              FS Interface (/proc)
 123   -------------------------------------------------------------------------- */
 124#ifdef CONFIG_ACPI_PROCFS
 125
 126static struct proc_dir_entry *acpi_fan_dir;
 127
 128static int acpi_fan_read_state(struct seq_file *seq, void *offset)
 129{
 130        struct acpi_device *device = seq->private;
 131        int state = 0;
 132
 133
 134        if (device) {
 135                if (acpi_bus_get_power(device->handle, &state))
 136                        seq_printf(seq, "status:                  ERROR\n");
 137                else
 138                        seq_printf(seq, "status:                  %s\n",
 139                                   !state ? "on" : "off");
 140        }
 141        return 0;
 142}
 143
 144static int acpi_fan_state_open_fs(struct inode *inode, struct file *file)
 145{
 146        return single_open(file, acpi_fan_read_state, PDE(inode)->data);
 147}
 148
 149static ssize_t
 150acpi_fan_write_state(struct file *file, const char __user * buffer,
 151                     size_t count, loff_t * ppos)
 152{
 153        int result = 0;
 154        struct seq_file *m = file->private_data;
 155        struct acpi_device *device = m->private;
 156        char state_string[3] = { '\0' };
 157
 158        if (count > sizeof(state_string) - 1)
 159                return -EINVAL;
 160
 161        if (copy_from_user(state_string, buffer, count))
 162                return -EFAULT;
 163
 164        state_string[count] = '\0';
 165        if ((state_string[0] < '0') || (state_string[0] > '3'))
 166                return -EINVAL;
 167        if (state_string[1] == '\n')
 168                state_string[1] = '\0';
 169        if (state_string[1] != '\0')
 170                return -EINVAL;
 171
 172        result = acpi_bus_set_power(device->handle,
 173                                    simple_strtoul(state_string, NULL, 0));
 174        if (result)
 175                return result;
 176
 177        return count;
 178}
 179
 180static const struct file_operations acpi_fan_state_ops = {
 181        .open = acpi_fan_state_open_fs,
 182        .read = seq_read,
 183        .write = acpi_fan_write_state,
 184        .llseek = seq_lseek,
 185        .release = single_release,
 186        .owner = THIS_MODULE,
 187};
 188
 189static int acpi_fan_add_fs(struct acpi_device *device)
 190{
 191        struct proc_dir_entry *entry = NULL;
 192
 193
 194        if (!device)
 195                return -EINVAL;
 196
 197        if (!acpi_device_dir(device)) {
 198                acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
 199                                                     acpi_fan_dir);
 200                if (!acpi_device_dir(device))
 201                        return -ENODEV;
 202        }
 203
 204        /* 'status' [R/W] */
 205        entry = proc_create_data(ACPI_FAN_FILE_STATE,
 206                                 S_IFREG | S_IRUGO | S_IWUSR,
 207                                 acpi_device_dir(device),
 208                                 &acpi_fan_state_ops,
 209                                 device);
 210        if (!entry)
 211                return -ENODEV;
 212        return 0;
 213}
 214
 215static int acpi_fan_remove_fs(struct acpi_device *device)
 216{
 217
 218        if (acpi_device_dir(device)) {
 219                remove_proc_entry(ACPI_FAN_FILE_STATE, acpi_device_dir(device));
 220                remove_proc_entry(acpi_device_bid(device), acpi_fan_dir);
 221                acpi_device_dir(device) = NULL;
 222        }
 223
 224        return 0;
 225}
 226#else
 227static int acpi_fan_add_fs(struct acpi_device *device)
 228{
 229        return 0;
 230}
 231
 232static int acpi_fan_remove_fs(struct acpi_device *device)
 233{
 234        return 0;
 235}
 236#endif
 237/* --------------------------------------------------------------------------
 238                                 Driver Interface
 239   -------------------------------------------------------------------------- */
 240
 241static int acpi_fan_add(struct acpi_device *device)
 242{
 243        int result = 0;
 244        int state = 0;
 245        struct thermal_cooling_device *cdev;
 246
 247        if (!device)
 248                return -EINVAL;
 249
 250        strcpy(acpi_device_name(device), "Fan");
 251        strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
 252
 253        result = acpi_bus_get_power(device->handle, &state);
 254        if (result) {
 255                printk(KERN_ERR PREFIX "Reading power state\n");
 256                goto end;
 257        }
 258
 259        device->flags.force_power_state = 1;
 260        acpi_bus_set_power(device->handle, state);
 261        device->flags.force_power_state = 0;
 262
 263        cdev = thermal_cooling_device_register("Fan", device,
 264                                                &fan_cooling_ops);
 265        if (IS_ERR(cdev)) {
 266                result = PTR_ERR(cdev);
 267                goto end;
 268        }
 269
 270        dev_info(&device->dev, "registered as cooling_device%d\n", cdev->id);
 271
 272        device->driver_data = cdev;
 273        result = sysfs_create_link(&device->dev.kobj,
 274                                   &cdev->device.kobj,
 275                                   "thermal_cooling");
 276        if (result)
 277                dev_err(&device->dev, "Failed to create sysfs link "
 278                        "'thermal_cooling'\n");
 279
 280        result = sysfs_create_link(&cdev->device.kobj,
 281                                   &device->dev.kobj,
 282                                   "device");
 283        if (result)
 284                dev_err(&device->dev, "Failed to create sysfs link "
 285                        "'device'\n");
 286
 287        result = acpi_fan_add_fs(device);
 288        if (result)
 289                goto end;
 290
 291        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 292               acpi_device_name(device), acpi_device_bid(device),
 293               !device->power.state ? "on" : "off");
 294
 295      end:
 296        return result;
 297}
 298
 299static int acpi_fan_remove(struct acpi_device *device, int type)
 300{
 301        struct thermal_cooling_device *cdev = acpi_driver_data(device);
 302
 303        if (!device || !cdev)
 304                return -EINVAL;
 305
 306        acpi_fan_remove_fs(device);
 307        sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 308        sysfs_remove_link(&cdev->device.kobj, "device");
 309        thermal_cooling_device_unregister(cdev);
 310
 311        return 0;
 312}
 313
 314static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state)
 315{
 316        if (!device)
 317                return -EINVAL;
 318
 319        acpi_bus_set_power(device->handle, ACPI_STATE_D0);
 320
 321        return AE_OK;
 322}
 323
 324static int acpi_fan_resume(struct acpi_device *device)
 325{
 326        int result = 0;
 327        int power_state = 0;
 328
 329        if (!device)
 330                return -EINVAL;
 331
 332        result = acpi_bus_get_power(device->handle, &power_state);
 333        if (result) {
 334                printk(KERN_ERR PREFIX
 335                                  "Error reading fan power state\n");
 336                return result;
 337        }
 338
 339        device->flags.force_power_state = 1;
 340        acpi_bus_set_power(device->handle, power_state);
 341        device->flags.force_power_state = 0;
 342
 343        return result;
 344}
 345
 346static int __init acpi_fan_init(void)
 347{
 348        int result = 0;
 349
 350
 351#ifdef CONFIG_ACPI_PROCFS
 352        acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
 353        if (!acpi_fan_dir)
 354                return -ENODEV;
 355#endif
 356
 357        result = acpi_bus_register_driver(&acpi_fan_driver);
 358        if (result < 0) {
 359                remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
 360                return -ENODEV;
 361        }
 362
 363        return 0;
 364}
 365
 366static void __exit acpi_fan_exit(void)
 367{
 368
 369        acpi_bus_unregister_driver(&acpi_fan_driver);
 370
 371        remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
 372
 373        return;
 374}
 375
 376module_init(acpi_fan_init);
 377module_exit(acpi_fan_exit);
 378
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.