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