All of lore.kernel.org
 help / color / mirror / Atom feed
* Acer Aspire One fan control
@ 2009-02-12 20:37 Peter Feuerer
  2009-02-12 21:03 ` Ralf Hildebrandt
                   ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-02-12 20:37 UTC (permalink / raw)
  To: LKML

Hi list,

I own such an Acer Aspire One netbook and the noisy fan annoyed me. (The 
default hardware controlled fan is on all the time). So I wrote a small 
kernel module which monitors the temperature of the cpu and turns the fan on 
and off. After testing it a while on my and some of my friends netbooks, it 
seems to be stable and I thought about submitting this functionality to 
the mainline kernel. As this is my first time, I've got some questions and 
would really appreciate any help.

Do you think it makes sense to add it as seperate kernelmodule or should I 
patch another module, e.g. the drivers/misc/acer-wmi.c module?
Should I try also to add the functionality of the 
/proc/acpi/thermal_zone/THRM/* things in my first patch? Or just submit the 
general functionality for controlling the fan and add additional things step 
by step?

kind regards, and many thanks for your answers.
--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-12 20:37 Acer Aspire One fan control Peter Feuerer
@ 2009-02-12 21:03 ` Ralf Hildebrandt
  2009-02-12 21:14   ` Peter Feuerer
  2009-02-13 10:35 ` Thomas Renninger
  2009-02-13 13:40 ` Matthew Garrett
  2 siblings, 1 reply; 18+ messages in thread
From: Ralf Hildebrandt @ 2009-02-12 21:03 UTC (permalink / raw)
  To: LKML

* Peter Feuerer <peter@piie.net>:
> Hi list,
>
> I own such an Acer Aspire One netbook and the noisy fan annoyed me. (The  
> default hardware controlled fan is on all the time). So I wrote a small  
> kernel module which monitors the temperature of the cpu and turns the fan 
> on and off. After testing it a while on my and some of my friends 
> netbooks, it seems to be stable and I thought about submitting this 
> functionality to the mainline kernel. As this is my first time, I've got 
> some questions and would really appreciate any help.

There's a userspace program for that. So why add a kernel module?

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-12 21:03 ` Ralf Hildebrandt
@ 2009-02-12 21:14   ` Peter Feuerer
  2009-03-09 10:54     ` Maxim Levitsky
  0 siblings, 1 reply; 18+ messages in thread
From: Peter Feuerer @ 2009-02-12 21:14 UTC (permalink / raw)
  To: Ralf Hildebrandt; +Cc: LKML

Ralf Hildebrandt writes:

> There's a userspace program for that. So why add a kernel module?

Because you must write to and read from io-ports and I think this should be 
done in the kernel. I guess the userspace program you are talking about is 
acer_ec.pl? A perl script which needs very much cpu time for a 
functionality which can be handled in kernelspace without causing any big 
troubles.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-12 20:37 Acer Aspire One fan control Peter Feuerer
  2009-02-12 21:03 ` Ralf Hildebrandt
@ 2009-02-13 10:35 ` Thomas Renninger
  2009-02-13 19:34   ` Peter Feuerer
  2009-02-13 13:40 ` Matthew Garrett
  2 siblings, 1 reply; 18+ messages in thread
From: Thomas Renninger @ 2009-02-13 10:35 UTC (permalink / raw)
  To: linux-kernel

Peter Feuerer <peter <at> piie.net> writes:

> 
> Hi list,
> 
> I own such an Acer Aspire One netbook and the noisy fan annoyed me. (The 
> default hardware controlled fan is on all the time). So I wrote a small 
> kernel module which monitors the temperature of the cpu and turns the fan on 
> and off. After testing it a while on my and some of my friends netbooks, it 
> seems to be stable and I thought about submitting this functionality to 
> the mainline kernel. As this is my first time, I've got some questions and 
> would really appreciate any help.
> 
> Do you think it makes sense to add it as seperate kernelmodule or should I 
> patch another module, e.g. the drivers/misc/acer-wmi.c module?
> Should I try also to add the functionality of the 
> /proc/acpi/thermal_zone/THRM/* things in my first patch? Or just submit the 
> general functionality for controlling the fan and add additional things step 
> by step?

Does it help if you do:
echo 10 >/proc/acpi/thermal_zone/*/polling_frequency

If the fan is controlled via thermal ACPI trip points, maybe latest kernel
versions do help? There were several commits lately in this area, e.g.:
676962dac6e267ce7c13f73962208f9124a084bb
f5adfaa372c76423b6e8e4727a9701330374f364
The latter adds a boot param you could give a try if you think it's worth it:
ACPI: Add "acpi.power_nocheck=1" to disable power state check in power
transition


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-12 20:37 Acer Aspire One fan control Peter Feuerer
  2009-02-12 21:03 ` Ralf Hildebrandt
  2009-02-13 10:35 ` Thomas Renninger
@ 2009-02-13 13:40 ` Matthew Garrett
  2009-02-13 18:59   ` Peter Feuerer
  2 siblings, 1 reply; 18+ messages in thread
From: Matthew Garrett @ 2009-02-13 13:40 UTC (permalink / raw)
  To: Peter Feuerer; +Cc: LKML

On Thu, Feb 12, 2009 at 09:37:13PM +0100, Peter Feuerer wrote:

> Do you think it makes sense to add it as seperate kernelmodule or should I 
> patch another module, e.g. the drivers/misc/acer-wmi.c module?
> Should I try also to add the functionality of the 
> /proc/acpi/thermal_zone/THRM/* things in my first patch? Or just submit the 
> general functionality for controlling the fan and add additional things 
> step by step?

That depends on how you're doing it. Is the code available somewhere? If 
these io ports are also used by the firmware then you'll need to find 
ACPI methods to call to perform the fan control. If there aren't any 
then it's not safe to have a driver to do this.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-13 13:40 ` Matthew Garrett
@ 2009-02-13 18:59   ` Peter Feuerer
  2009-02-13 19:18     ` Matthew Garrett
  0 siblings, 1 reply; 18+ messages in thread
From: Peter Feuerer @ 2009-02-13 18:59 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: LKML

Matthew Garrett writes:

> That depends on how you're doing it. Is the code available somewhere? 

Just cleaned up the code and released it on my homepage, you can download 
it from: http://piie.net/files/acerhdf_kmod-0.2.tar.gz

> If 
> these io ports are also used by the firmware then you'll need to find 
> ACPI methods to call to perform the fan control. 

I get the temperature by calling ec_read(0x58) and can read/write the state 
of the fan by ec_write to / ec_read from 0x55. Actually I don't know if 
those embedded controller registers are used by the firmware. How can I find 
it out?

> If there aren't any 
> then it's not safe to have a driver to do this.

I think there should not be a problem when calling ec_write and ec_read 
from my kernel module, as those functions are atomic. Or am I wrong?

--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-13 18:59   ` Peter Feuerer
@ 2009-02-13 19:18     ` Matthew Garrett
  2009-02-13 19:45       ` Peter Feuerer
  2009-02-27 18:58       ` [PATCH] acerhdf: " Peter Feuerer
  0 siblings, 2 replies; 18+ messages in thread
From: Matthew Garrett @ 2009-02-13 19:18 UTC (permalink / raw)
  To: Peter Feuerer; +Cc: LKML

On Fri, Feb 13, 2009 at 07:59:16PM +0100, Peter Feuerer wrote:
> Matthew Garrett writes:
> >If 
> >these io ports are also used by the firmware then you'll need to find 
> >ACPI methods to call to perform the fan control. 
> 
> I get the temperature by calling ec_read(0x58) and can read/write the state 
> of the fan by ec_write to / ec_read from 0x55. Actually I don't know if 
> those embedded controller registers are used by the firmware. How can I 
> find it out?

Ok, yup, that should be safe. It might be nice to implement this as an 
hwmon driver, potentially tying it into the thermal layer.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-13 10:35 ` Thomas Renninger
@ 2009-02-13 19:34   ` Peter Feuerer
  0 siblings, 0 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-02-13 19:34 UTC (permalink / raw)
  To: Thomas Renninger; +Cc: linux-kernel

Thomas Renninger writes:

> 
> Does it help if you do:
> echo 10 >/proc/acpi/thermal_zone/*/polling_frequency

Actually there is nothing in /proc/acpi/thermal_zone/. So it seems for me, 
that there is no driver which handles the temperature and the fan of the 
Acer Aspire One yet. A search on google didn't disprove it. That's why I 
started to write my own kernel module. But I tested it only with 2.6.28.4. 
Maybe there's already something in 2.6.29, not sure about it. 

> If the fan is controlled via thermal ACPI trip points, maybe latest kernel
> versions do help? There were several commits lately in this area, e.g.:
> 676962dac6e267ce7c13f73962208f9124a084bb
> f5adfaa372c76423b6e8e4727a9701330374f364

Those patches have been applied to 2.6.28.

> The latter adds a boot param you could give a try if you think it's worth it:
> ACPI: Add "acpi.power_nocheck=1" to disable power state check in power
> transition

I will try the acpi.power_nocheck option, but google results on that don't 
sound very promising.

--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-13 19:18     ` Matthew Garrett
@ 2009-02-13 19:45       ` Peter Feuerer
  2009-02-27 18:58       ` [PATCH] acerhdf: " Peter Feuerer
  1 sibling, 0 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-02-13 19:45 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: LKML

Matthew Garrett writes:

>> I get the temperature by calling ec_read(0x58) and can read/write the state 
>> of the fan by ec_write to / ec_read from 0x55. Actually I don't know if 
>> those embedded controller registers are used by the firmware. How can I 
>> find it out?
> 
> Ok, yup, that should be safe. It might be nice to implement this as an 
> hwmon driver, potentially tying it into the thermal layer.

Thank you very much, I will have a deeper look into hwmon and the thermal 
layer.

--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH] acerhdf: Acer Aspire One fan control
  2009-02-13 19:18     ` Matthew Garrett
  2009-02-13 19:45       ` Peter Feuerer
@ 2009-02-27 18:58       ` Peter Feuerer
       [not found]         ` <1235763653.24506.6.camel@localhost>
  1 sibling, 1 reply; 18+ messages in thread
From: Peter Feuerer @ 2009-02-27 18:58 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: LKML

Matthew Garrett writes:

> Ok, yup, that should be safe. It might be nice to implement this as an 
> hwmon driver, potentially tying it into the thermal layer.

Here is a patch which adds the "acerhdf" module to the kernel. It is now 
tied into the thermal layer.

The module creates a cooling device and a thermal zone. Both can be 
accessed through /sys/class/thermal/*. The module can be ran either in 
"kernel mode" in which a kernelthread to monitor the temperature and control 
the fan is started. Or it can be ran in "user mode" in which an 
userspace application can handle the fan control.

What do you think about this piece of code?

kind regards and thanks for all your comments!

--peter


diff -Naur linux-2.6.28_original/drivers/misc/Kconfig linux-2.6.28_foo/drivers/misc/Kconfig
--- linux-2.6.28_original/drivers/misc/Kconfig	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28_foo/drivers/misc/Kconfig	2009-02-27 19:30:40.000000000 +0100
@@ -158,6 +158,18 @@
 	  If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
 	  here.
 
+config ACERHDF
+        tristate "Acer Aspire One Fan Control (EXPERIMENTAL)"
+	depends on X86
+	depends on EXPERIMENTAL
+	depends on ACPI
+	depends on THERMAL
+	---help---
+      This is the driver for monitoring the temperature and controlling
+      the fan of Acer Aspire One netbooks.
+      The temperature can be read from
+      /sys/class/thermal/thermal_zone0/temp
+
 config ASUS_LAPTOP
         tristate "Asus Laptop Extras (EXPERIMENTAL)"
         depends on X86
diff -Naur linux-2.6.28_original/drivers/misc/Makefile linux-2.6.28_foo/drivers/misc/Makefile
--- linux-2.6.28_original/drivers/misc/Makefile	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28_foo/drivers/misc/Makefile	2009-02-19 22:57:16.000000000 +0100
@@ -10,6 +10,7 @@
 obj-$(CONFIG_MSI_LAPTOP)	+= msi-laptop.o
 obj-$(CONFIG_COMPAL_LAPTOP)	+= compal-laptop.o
 obj-$(CONFIG_ACER_WMI)		+= acer-wmi.o
+obj-$(CONFIG_ACERHDF)		+= acerhdf.o
 obj-$(CONFIG_ATMEL_PWM)		+= atmel_pwm.o
 obj-$(CONFIG_ATMEL_SSC)		+= atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o
diff -Naur linux-2.6.28_original/drivers/misc/acerhdf.c linux-2.6.28_foo/drivers/misc/acerhdf.c
--- linux-2.6.28_original/drivers/misc/acerhdf.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.28_foo/drivers/misc/acerhdf.c	2009-02-27 19:32:40.000000000 +0100
@@ -0,0 +1,560 @@
+/*
+ * acerhdf - A kernelmodule which monitors the temperature 
+ *           of the aspire one netbook, turns on/off the fan
+ *           as soon as the upper/lower threshold is reached.
+ *
+ * (C) 2009 - Peter Feuerer     peter (a) piie.net
+ *            http://piie.net
+ *
+ *
+ * 
+ * Inspired by and many thanks to:
+ *    o acerfand   - Rachel Greenham
+ *    o acer_ec.pl - Michael Kurz     michi.kurz (at) googlemail.com
+ *                 - Petr Tomasek     tomasek (#) etf,cuni,cz
+ *                 - Carlos Corbacho  cathectic (at) gmail.com
+ *          
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or 
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *  06-February-2009: Version 0.1:
+ *    - first relase, containing absolutely no bugs! ;)
+ *
+ *  06-February-2009: Version 0.1.1:
+ *    - found first bug :-) - it didn't check the bios vendor
+ *    - check if the bios vendor is Acer
+ *    - added bios 3301
+ *
+ *  06-February-2009: Version 0.1.2:
+ *    - added fork for deamon mode, now a real daemon is spawned
+ *    - added device vendor "INSYDE"
+ *
+ *  13-February-2009: Version 0.2:
+ *    - ported to kernelspace
+ *
+ *  19-February-2009: Version 0.2.1:
+ *    - added Bios Version 3308
+ *    - cleaned up the includes
+ *
+ *  21-February-2009: Version 0.2.2:
+ *    - changed settings for Bios 3309 as old settings caused lock ups
+ *      - thanks to Frank Reimann
+ *
+ *  21-February-2009: Version 0.2.2-2:
+ *    - added linux/sched.h to includes again, as it won't compile for
+ *      kernel < 2.6.28 without it.
+ *
+ *  23-February-2009: Version 0.3:
+ *    - tied to termal layer
+ *    - added parameters to /sys/modules/acerhdf/parameters/
+ *
+ *  25-February-2009: Version 0.3.1:
+ *    - fixed starting the module in user mode when force_bios param
+ *      is given
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/dmi.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/sched.h>
+#include <linux/thermal.h>
+
+#define VERSION "0.3.1"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Feuerer");
+
+/* thread handling variables */
+static int thread_id=0;
+static struct pid * thread_pid=NULL;
+static wait_queue_head_t wq;
+static DECLARE_COMPLETION( on_exit );
+
+/* global variables */
+static int interval=10;
+static int fanon=67;
+static int fanoff=62;
+static int verbose=0;
+static int kernelmode=1;
+static int fanstate=1;
+static int bios_version=-1;
+static char force_bios[16];
+struct thermal_zone_device * acerhdf_thz_dev;
+struct thermal_cooling_device * acerhdf_cool_dev;
+
+/* module parameters */
+module_param(interval, int, 0600);
+MODULE_PARM_DESC(interval, "Polling interval of temperatur check");
+module_param(fanon, int, 0600);
+MODULE_PARM_DESC(fanon, "Above which temperature should the fan be started");
+module_param(fanoff, int, 0600);
+MODULE_PARM_DESC(fanoff, "Below which temperature should the fan be stopped");
+module_param(verbose, int, 0600);
+MODULE_PARM_DESC(verbose, "Enable verbose dmesg outputs");
+module_param_string(force_bios,force_bios,16,0);
+MODULE_PARM_DESC(force_bios, "Force bios version and omit bios check");
+
+/* bios settings */
+/**********************************************************************/
+typedef struct
+{
+   char version[10];
+   unsigned char fanreg;
+   unsigned char tempreg;
+   unsigned char cmd_off;
+   unsigned char cmd_auto;
+   unsigned char state_off;
+} bios_settings_t;
+
+/* some bios versions have different commands and
+ * maybe also different register addresses */
+static bios_settings_t bios_settings[]=
+{
+   {"v0.3109",0x55,0x58,0x1f,0x00,0x1f},
+   {"v0.3114",0x55,0x58,0x1f,0x00,0x1f},
+   {"v0.3301",0x55,0x58,0xaf,0x00,0xaf},
+   {"v0.3304",0x55,0x58,0xaf,0x00,0xaf},
+   {"v0.3305",0x55,0x58,0xaf,0x00,0xaf},
+   {"v0.3308",0x55,0x58,0xaf,0x00,0xaf},
+   {"v0.3309",0x55,0x58,0x21,0x00,0x21},
+   {"",0,0,0,0,0}
+};
+
+
+/* acer ec functions */
+/**********************************************************************/
+/* switch on/off the fan */
+static void change_fanstate(int state)
+{
+   if(verbose)
+   {
+      printk("acerhdf: fan %s\n",state?"ON":"OFF");
+   }
+   ec_write(bios_settings[bios_version].fanreg, 
+         state? bios_settings[bios_version].cmd_auto:
+         bios_settings[bios_version].cmd_off);
+}
+
+/* thread to monitor the temperature and control the fan */
+static int acerhdf_thread( void *data )
+{
+   unsigned long timeout;
+   u8 temp=0;
+   u8 last_temp=0;
+   int unchanged_cnt=0;
+
+   fanstate=1;
+   daemonize("acerhdf");
+   allow_signal( SIGTERM ); 
+   thread_pid=task_pid(current);
+   for(;;) 
+   {
+      if(!ec_read(bios_settings[bios_version].tempreg,&temp))
+      {
+         /* print temperature in verbose mode */
+         if(verbose)
+         {
+            printk("acerhdf: Temperature is: %d\n",temp);
+         }
+         /* if temperature is greater than fanon, switch on fan */
+         if(temp>=fanon && fanstate==0)
+         {
+            change_fanstate(1);
+            fanstate=1;
+         }
+         /* if temperature is less than fanoff, switch off fan */
+         else if(temp<fanoff && fanstate==1)
+         {
+            change_fanstate(0);
+            fanstate=0;
+         }
+            
+      }
+
+      /* sleep interval seconds */
+      timeout=HZ*interval;
+      timeout=wait_event_interruptible_timeout(wq, 
+            (timeout==0), timeout);
+
+      /* if wait was interrupted by SIGTERM, end thread */
+      if( timeout==-ERESTARTSYS ) 
+      {
+         printk("acerhdf: ending\n");
+         break;
+      }
+
+      /* check if read temperature is reasonable. If not, change to user mode
+       * and turn on fan to save the hardware */
+      if(last_temp==temp && temp < 30)
+      {
+         if(unchanged_cnt++ >= 10)
+         {
+            printk("acerhdf: cannot read temperature, switching to user mode\n");
+            kernelmode=0;
+            break;
+         }
+      }
+      else
+      {
+         unchanged_cnt=0;
+      }
+      last_temp=temp;
+   }
+   /* turn on fan before ending the thread */
+   change_fanstate(1);
+   thread_id = 0;
+   complete_and_exit( &on_exit, 0 );
+}
+
+/* thermal zone callback functions */
+/**********************************************************************/
+static int get_ec_temp(struct thermal_zone_device *thermal, char *buf)
+{
+   u8 temp;
+   /* return temperature */
+   if(!ec_read(bios_settings[bios_version].tempreg,&temp))
+   {
+      return sprintf(buf,"%d\n",temp);
+   }
+   return -EINVAL;
+}
+
+/* bind the cooling device to the thermal zone */
+static int bind(struct thermal_zone_device *thermal,
+                     struct thermal_cooling_device *cdev)
+{
+   /* if the cooling device is the one from acerhdf bind it */
+   if(cdev==acerhdf_cool_dev)
+   {
+      if(thermal_zone_bind_cooling_device(thermal,0,cdev))
+      {
+         printk("acerhdf: error binding cooling dev to trip 0\n");
+         return -EINVAL;
+      }
+      if(thermal_zone_bind_cooling_device(thermal,1,cdev))
+      {
+         printk("acerhdf: error binding cooling dev to trip 1\n");
+         return -EINVAL;
+      }
+   }
+   return 0;
+}
+
+/* unbind cooling device from thermal zone */
+static int unbind(struct thermal_zone_device *thermal,
+                     struct thermal_cooling_device *cdev)
+{
+   if(cdev==acerhdf_cool_dev)
+   {
+      if(thermal_zone_unbind_cooling_device(thermal,0,cdev))
+      {
+         printk("acerhdf: error unbinding cooling dev of trip 0\n");
+         return -EINVAL;
+      }
+      if(thermal_zone_unbind_cooling_device(thermal,1,cdev))
+      {
+         printk("acerhdf: error unbinding cooling dev of trip 1\n");
+         return -EINVAL;
+      }
+   }
+   return 0;
+}
+
+/* print currend operation mode - kernel / user */
+static int get_mode(struct thermal_zone_device *thermal,
+				char *buf)
+{
+   if(!kernelmode)
+   {
+      return sprintf(buf,"user\n");
+   }
+   else
+   {
+      return sprintf(buf,"kernel\n");
+   }
+   return 0;
+}
+
+/* set operation mode; 
+ * kernel: a kernel thread takes care about managing the 
+ *    fan (see acerhdf_thread)
+ * user: kernel thread is stopped and a userspace tool 
+ *    should take care about managing the fan
+ */
+static int set_mode(struct thermal_zone_device *thermal,
+				const char *buf)
+{
+   /* set mode to user mode */
+   if(!strncmp(buf,"user",4))
+   {
+      if(verbose)
+      {
+         printk("acerhdf: set to usermode\n");
+      }
+      /* send SIGTERM to thread and wait until thread died */
+      if(thread_pid) 
+      {
+         kill_pid(thread_pid,SIGTERM,1);
+      }
+      wait_for_completion( &on_exit );
+      kernelmode=0;
+      return 0;
+   }
+   /* set to kernel mode */
+   else if(!strncmp(buf,"kernel",6))
+   {
+      /* start acerhdf_thread */
+      if(!kernelmode && !thread_id)
+      {
+         thread_id=kernel_thread(acerhdf_thread, NULL, CLONE_KERNEL );
+         if( thread_id==0 )
+         {
+            return -EIO;
+         }
+      }
+      if(verbose)
+      {
+         printk("acerhdf: set to kernelmode\n");
+      }
+      kernelmode=1;  
+      return 0;
+   }
+   return -EINVAL;
+}
+
+/* print the name of the trip point */
+static int get_trip_type(struct thermal_zone_device *thermal,
+				 int trip, char *buf)
+{
+   if(trip==0)
+   {
+      return sprintf(buf,"fanoff\n");
+   }
+   else if(trip==1)
+   {
+      return sprintf(buf,"fanon\n");
+   }
+   return 0;
+}
+
+/* print the temperature at which the trip point gets active */
+static int get_trip_temp(struct thermal_zone_device *thermal,
+				 int trip, char *buf)
+{
+   if(trip==0)
+   {
+      return sprintf(buf,"%d\n",fanoff);
+   }
+   else if(trip==1)
+   {
+      return sprintf(buf,"%d\n",fanon);
+   }
+   return 0;
+}
+
+static int get_crit_temp(struct thermal_zone_device *thermal,
+				unsigned long *temperature)
+{
+   return 0;
+}
+
+/* bind callback functions to thermalzone */
+struct thermal_zone_device_ops acerhdf_device_ops =
+{
+	.bind = bind,
+   .unbind=unbind,
+   .get_temp=get_ec_temp,
+	.get_mode = get_mode,
+	.set_mode = set_mode,
+	.get_trip_type = get_trip_type,
+	.get_trip_temp = get_trip_temp,
+	.get_crit_temp = get_crit_temp,
+};
+
+
+/* cooling device callback functions */
+/**********************************************************************/
+/* print maximal fan cooling state */
+static int get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+   return sprintf(buf,"1\n");
+}
+
+/* print current fan state */
+static int get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+   u8 fan;
+   if(!ec_read(bios_settings[bios_version].fanreg,&fan))
+   {
+      return sprintf(buf,"%d\n",
+            (fan==bios_settings[bios_version].cmd_auto)?1:0);
+   }
+   return 0;
+}
+
+/* change current fan state - is overwritten when running in kernel mode */
+static int set_cur_state(struct thermal_cooling_device *cdev, 
+      unsigned int state)
+{
+   if(kernelmode)
+   {
+      printk("acerhdf: changing the fanstate in kernelmode is not allowed\n");
+      return -EINVAL;
+   }
+   if(verbose)
+   {
+      printk("acerhdf: set fan: %d\n",state);
+   }
+   change_fanstate(state);
+   return 0;
+}
+
+/* bind fan callbacks to fan device */
+struct thermal_cooling_device_ops acerhdf_cooling_ops =
+{
+   .get_max_state=get_max_state,
+   .get_cur_state=get_cur_state,
+   .set_cur_state=set_cur_state,
+};
+
+
+/* kernel module init / exit functions */
+/**********************************************************************/
+/* initialize the module */
+static int __init acerhdf_init(void)
+{
+   char const *vendor;
+   char const *version;
+   char const *release;
+   char const *product;
+   int i;
+
+
+   /* get bios data */
+   vendor=dmi_get_system_info(DMI_SYS_VENDOR);
+   version=dmi_get_system_info(DMI_BIOS_VERSION);
+   release=dmi_get_system_info(DMI_BIOS_DATE);
+   product=dmi_get_system_info(DMI_PRODUCT_NAME);
+
+
+   /* print out bios data */
+   printk("acerhdf: version: %s compiledate: %s %s\n",
+         VERSION,__DATE__,__TIME__);
+   printk("acerhdf: README: http://piie.net/files/acerhdf_README.txt\n");
+   printk("acerhdf: biosvendor:%s\n",vendor);
+   printk("acerhdf: biosversion:%s\n",version);
+   printk("acerhdf: biosrelease:%s\n",release);
+   printk("acerhdf: biosproduct:%s\n",product);
+
+   if(!force_bios[0])
+   {
+
+      /* check if vendor of the hardware is Acer */
+      if(strcmp(vendor,"Acer"))
+      {
+         printk("acerhdf: no Acer hardware found\n");
+         return -ENODEV;
+      }
+      /* check if product is a AO - Aspire One */
+      if(strncmp(product,"AO",2))
+      {
+         printk("acerhdf: no Aspire One hardware found\n");
+         return -ENODEV;
+      }
+   }
+   else
+   {
+      printk("acerhdf: bios version: %s forced, kernelmode disabled\n",version);
+      printk("acerhdf: to enable kernelmode:\n");      
+      printk("acerhdf: echo kernel > /sys/class/thermal/thermal_zone0/mode\n");
+      version=force_bios;
+      kernelmode=0;
+   }
+
+   /* search bios in bios settings table */
+   for(i=0;bios_settings[i].version[0];++i)
+   {
+      if(!strcmp(bios_settings[i].version,version))
+      {
+         bios_version=i;
+         break;
+      }
+   }
+   if(bios_version==-1)
+   {
+      printk("acerhdf: cannot find bios version\n");
+      return -ENODEV;
+   }
+   
+   /* create cooling device */
+   acerhdf_cool_dev=thermal_cooling_device_register("acerhdf-fan",NULL,
+         &acerhdf_cooling_ops);
+   if(IS_ERR(acerhdf_cool_dev))
+   {
+      return -ENODEV;
+   }
+
+   /* create thermal zone */
+   acerhdf_thz_dev=thermal_zone_device_register("acerhdf",2,
+         NULL,&acerhdf_device_ops);
+   if(IS_ERR(acerhdf_thz_dev))
+   {
+      return -ENODEV;
+   }
+
+   init_waitqueue_head(&wq);
+   /* start acerhdf_thread */
+   if(kernelmode)
+   {
+      thread_id=kernel_thread(acerhdf_thread, NULL, CLONE_KERNEL );
+      if( thread_id==0 )
+      {
+         return -EIO;
+      }
+   }
+
+	return 0;
+}
+
+/* exit the module */
+static void __exit acerhdf_exit(void)
+{
+   /* unregister thermal zone */
+   if(acerhdf_thz_dev)
+   {
+      thermal_zone_device_unregister(acerhdf_thz_dev);
+      acerhdf_thz_dev=NULL;
+   }
+   /* unregister cooling device */
+   if(acerhdf_cool_dev)
+   {
+      thermal_cooling_device_unregister(acerhdf_cool_dev);
+      acerhdf_cool_dev=NULL;
+   }
+   /* send SIGTERM to thread */
+   if(thread_pid) 
+   {
+      kill_pid(thread_pid,SIGTERM,1);
+      wait_for_completion( &on_exit );
+   }
+}
+
+/* what are the module init/exit functions */
+module_init( acerhdf_init );
+module_exit( acerhdf_exit );

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
       [not found]         ` <1235763653.24506.6.camel@localhost>
@ 2009-02-28 18:58           ` Peter Feuerer
  2009-03-01 14:38             ` Matthew Garrett
  2009-03-01 15:27             ` Ed Tomlinson
  0 siblings, 2 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-02-28 18:58 UTC (permalink / raw)
  To: Joe Perches; +Cc: LKML

Hi Joe, Hi List,

thank you very much for your tips!

Joe Perches writes:
> On Fri, 2009-02-27 at 19:58 +0100, Peter Feuerer wrote:
>> What do you think about this piece of code?
> 
> I think you should run it through lindent/checkpatch.

I let the checkpatch.pl script ran over the code and corrected every single 
warning and error. The resulting code is at the end of this email.

Do you (or anyobdy else on this list) have any further tips, suggestions or 
advices? If everybody is fine with the code, what will be the next steps?

kind regards,
--peter



diff -Naur linux-2.6.28_original/drivers/misc/Kconfig linux-2.6.28_foo/drivers/misc/Kconfig
--- linux-2.6.28_original/drivers/misc/Kconfig	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28_foo/drivers/misc/Kconfig	2009-02-27 19:30:40.000000000 +0100
@@ -158,6 +158,18 @@
 	  If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
 	  here.
 
+config ACERHDF
+        tristate "Acer Aspire One Fan Control (EXPERIMENTAL)"
+	depends on X86
+	depends on EXPERIMENTAL
+	depends on ACPI
+	depends on THERMAL
+	---help---
+      This is the driver for monitoring the temperature and controlling
+      the fan of Acer Aspire One netbooks.
+      The temperature can be read from
+      /sys/class/thermal/thermal_zone0/temp
+
 config ASUS_LAPTOP
         tristate "Asus Laptop Extras (EXPERIMENTAL)"
         depends on X86
diff -Naur linux-2.6.28_original/drivers/misc/Makefile linux-2.6.28_foo/drivers/misc/Makefile
--- linux-2.6.28_original/drivers/misc/Makefile	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28_foo/drivers/misc/Makefile	2009-02-19 22:57:16.000000000 +0100
@@ -10,6 +10,7 @@
 obj-$(CONFIG_MSI_LAPTOP)	+= msi-laptop.o
 obj-$(CONFIG_COMPAL_LAPTOP)	+= compal-laptop.o
 obj-$(CONFIG_ACER_WMI)		+= acer-wmi.o
+obj-$(CONFIG_ACERHDF)		+= acerhdf.o
 obj-$(CONFIG_ATMEL_PWM)		+= atmel_pwm.o
 obj-$(CONFIG_ATMEL_SSC)		+= atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o
diff -Naur linux-2.6.28_original/drivers/misc/acerhdf.c linux-2.6.28_foo/drivers/misc/acerhdf.c
--- linux-2.6.28_original/drivers/misc/acerhdf.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.28_foo/drivers/misc/acerhdf.c	2009-02-28 19:34:55.000000000 +0100
@@ -0,0 +1,527 @@
+/*
+ * acerhdf - A kernelmodule which monitors the temperature
+ *           of the aspire one netbook, turns on/off the fan
+ *           as soon as the upper/lower threshold is reached.
+ *
+ * (C) 2009 - Peter Feuerer     peter (a) piie.net
+ *                              http://piie.net
+ *
+ *
+ *
+ * Inspired by and many thanks to:
+ *  o acerfand   - Rachel Greenham
+ *  o acer_ec.pl - Michael Kurz     michi.kurz (at) googlemail.com
+ *               - Petr Tomasek     tomasek (#) etf,cuni,cz
+ *               - Carlos Corbacho  cathectic (at) gmail.com
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *  06-February-2009: Version 0.1:
+ *  - first relase, containing absolutely no bugs! ;)
+ *
+ *  06-February-2009: Version 0.1.1:
+ *  - found first bug :-) - it didn't check the bios vendor
+ *  - check if the bios vendor is Acer
+ *  - added bios 3301
+ *
+ *  06-February-2009: Version 0.1.2:
+ *  - added fork for deamon mode, now a real daemon is spawned
+ *  - added device vendor "INSYDE"
+ *
+ *  13-February-2009: Version 0.2:
+ *  - ported to kernelspace
+ *
+ *  19-February-2009: Version 0.2.1:
+ *  - added Bios Version 3308
+ *  - cleaned up the includes
+ *
+ *  21-February-2009: Version 0.2.2:
+ *  - changed settings for Bios 3309 as old settings caused lock ups
+ *     - thanks to Frank Reimann
+ *
+ *  21-February-2009: Version 0.2.2-2:
+ *  - added linux/sched.h to includes again, as it won't compile for
+ *    kernel < 2.6.28 without it.
+ *
+ *  23-February-2009: Version 0.3:
+ *  - tied to termal layer
+ *  - added parameters to /sys/modules/acerhdf/parameters/
+ *
+ *  25-February-2009: Version 0.3.1:
+ *  - fixed starting the module in user mode when force_bios param
+ *    is given
+ *
+ *  28-February-2009: Version 0.3.2:
+ *  - changed coding style to fit the coding style of the kernel
+ *    and checked it via checkpatch
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/dmi.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/sched.h>
+#include <linux/thermal.h>
+
+#define VERSION "0.3.2"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Feuerer");
+
+/* thread handling variables */
+static int thread_id;
+static struct pid *thread_pid;
+static wait_queue_head_t wq;
+static DECLARE_COMPLETION(on_exit);
+
+/* global variables */
+static int interval = 10;
+static int fanon = 67;
+static int fanoff = 62;
+static int verbose;
+static int kernelmode = 1;
+static int fanstate = 1;
+static int bios_version = -1;
+static char force_bios[16];
+struct thermal_zone_device *acerhdf_thz_dev;
+struct thermal_cooling_device *acerhdf_cool_dev;
+
+/* module parameters */
+module_param(interval, int, 0600);
+MODULE_PARM_DESC(interval, "Polling interval of temperature check");
+module_param(fanon, int, 0600);
+MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
+module_param(fanoff, int, 0600);
+MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
+module_param(verbose, int, 0600);
+MODULE_PARM_DESC(verbose, "Enable verbose dmesg outputs");
+module_param_string(force_bios, force_bios, 16, 0);
+MODULE_PARM_DESC(force_bios, "Force bios version and omit bios check");
+
+/* bios settings */
+/**********************************************************************/
+struct bios_settings_t {
+	const char version[10];
+	unsigned char fanreg;
+	unsigned char tempreg;
+	unsigned char cmd_off;
+	unsigned char cmd_auto;
+	unsigned char state_off;
+};
+
+/* some bios versions have different commands and
+ * maybe also different register addresses */
+static const struct bios_settings_t bios_settings[] = {
+	{"v0.3109", 0x55, 0x58, 0x1f, 0x00, 0x1f},
+	{"v0.3114", 0x55, 0x58, 0x1f, 0x00, 0x1f},
+	{"v0.3301", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"v0.3304", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"v0.3305", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"v0.3308", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"v0.3309", 0x55, 0x58, 0x21, 0x00, 0x21},
+	{"", 0, 0, 0, 0, 0}
+};
+
+
+/* acer ec functions */
+/**********************************************************************/
+/* switch on/off the fan */
+static void change_fanstate(int state)
+{
+	if (verbose)
+		printk(KERN_NOTICE "acerhdf: fan %s\n", state ? "ON" : "OFF");
+
+	ec_write(bios_settings[bios_version].fanreg,
+			state ? bios_settings[bios_version].cmd_auto :
+			bios_settings[bios_version].cmd_off);
+}
+
+/* thread to monitor the temperature and control the fan */
+static int acerhdf_thread(void *data)
+{
+	unsigned long timeout;
+	u8 temp = 0;
+	u8 last_temp = 0;
+	int unchanged_cnt = 0;
+
+	fanstate = 1;
+	daemonize("acerhdf");
+	allow_signal(SIGTERM);
+	thread_pid = task_pid(current);
+	for (;;) {
+		if (!ec_read(bios_settings[bios_version].tempreg, &temp)) {
+			/* print temperature in verbose mode */
+			if (verbose)
+				printk(KERN_NOTICE "acerhdf: Temperature is: %d\n",
+					temp);
+
+			/* if temperature is greater than fanon,
+			 * switch on fan */
+			if (temp >= fanon && fanstate == 0) {
+				change_fanstate(1);
+				fanstate = 1;
+			}
+			/* if temperature is less than fanoff,
+			 * switch off fan */
+			else if (temp < fanoff && fanstate == 1) {
+				change_fanstate(0);
+				fanstate = 0;
+			}
+		}
+
+		/* sleep interval seconds */
+		timeout = HZ*interval;
+		timeout = wait_event_interruptible_timeout(wq,
+				(timeout == 0), timeout);
+
+		/* if wait was interrupted by SIGTERM, end thread */
+		if (timeout == -ERESTARTSYS) {
+			printk(KERN_NOTICE "acerhdf: ending kernelmode\n");
+			break;
+		}
+
+		/* check if read temperature is reasonable. If not,
+		 * change to user mode and turn on fan to save the
+		 * hardware */
+		if (last_temp == temp && temp < 30) {
+			if (unchanged_cnt++ >= 10) {
+				printk(KERN_ERR
+					"acerhdf: cannot read temperature:\n");
+				printk(KERN_ERR
+					"acerhdf: switching to user mode\n");
+				kernelmode = 0;
+				break;
+			}
+		} else
+			unchanged_cnt = 0;
+
+		last_temp = temp;
+	}
+	/* turn on fan before ending the thread */
+	change_fanstate(1);
+	thread_id = 0;
+	thread_pid = NULL;
+	complete_and_exit(&on_exit, 0);
+}
+
+/* thermal zone callback functions */
+/**********************************************************************/
+static int get_ec_temp(struct thermal_zone_device *thermal, char *buf)
+{
+	u8 temp;
+	/* return temperature */
+	if (!ec_read(bios_settings[bios_version].tempreg, &temp))
+		return sprintf(buf, "%d\n", temp);
+
+	return -EINVAL;
+}
+
+/* bind the cooling device to the thermal zone */
+static int bind(struct thermal_zone_device *thermal,
+		struct thermal_cooling_device *cdev)
+{
+	/* if the cooling device is the one from acerhdf bind it */
+	if (cdev == acerhdf_cool_dev) {
+		if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error binding cooling dev\n");
+			return -EINVAL;
+		}
+		if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error binding cooling dev\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* unbind cooling device from thermal zone */
+static int unbind(struct thermal_zone_device *thermal,
+		struct thermal_cooling_device *cdev)
+{
+	if (cdev == acerhdf_cool_dev) {
+		if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error unbinding cooling dev\n");
+			return -EINVAL;
+		}
+		if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error unbinding cooling dev\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* print currend operation mode - kernel / user */
+static int get_mode(struct thermal_zone_device *thermal,
+		char *buf)
+{
+	if (!kernelmode)
+		return sprintf(buf, "user\n");
+
+	else
+		return sprintf(buf, "kernel\n");
+
+	return 0;
+}
+
+/* set operation mode;
+ * kernel: a kernel thread takes care about managing the
+ *	 fan (see acerhdf_thread)
+ * user: kernel thread is stopped and a userspace tool
+ *	 should take care about managing the fan
+ */
+static int set_mode(struct thermal_zone_device *thermal,
+		const char *buf)
+{
+	/* set mode to user mode */
+	if (!strncmp(buf, "user", 4)) {
+		if (verbose)
+			printk(KERN_NOTICE "acerhdf: set to usermode\n");
+
+		/* send SIGTERM to thread and wait until thread died */
+		if (thread_pid)
+			kill_pid(thread_pid, SIGTERM, 1);
+
+		wait_for_completion(&on_exit);
+		kernelmode = 0;
+		return 0;
+	}
+	/* set to kernel mode */
+	else if (!strncmp(buf, "kernel", 6)) {
+		/* start acerhdf_thread */
+		if (!kernelmode && !thread_id) {
+			thread_id = kernel_thread(acerhdf_thread, NULL,
+					CLONE_KERNEL);
+
+			if (thread_id == 0)
+				return -EIO;
+		}
+
+		if (verbose)
+			printk(KERN_NOTICE "acerhdf: set to kernelmode\n");
+
+		kernelmode = 1;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+/* print the name of the trip point */
+static int get_trip_type(struct thermal_zone_device *thermal,
+		int trip, char *buf)
+{
+	if (trip == 0)
+		return sprintf(buf, "fanoff\n");
+
+	else if (trip == 1)
+		return sprintf(buf, "fanon\n");
+
+	return 0;
+}
+
+/* print the temperature at which the trip point gets active */
+static int get_trip_temp(struct thermal_zone_device *thermal,
+		int trip, char *buf)
+{
+	if (trip == 0)
+		return sprintf(buf, "%d\n", fanoff);
+
+	else if (trip == 1)
+		return sprintf(buf, "%d\n", fanon);
+
+	return 0;
+}
+
+static int get_crit_temp(struct thermal_zone_device *thermal,
+		unsigned long *temperature)
+{
+	return 0;
+}
+
+/* bind callback functions to thermalzone */
+struct thermal_zone_device_ops acerhdf_device_ops = {
+	.bind = bind,
+	.unbind = unbind,
+	.get_temp = get_ec_temp,
+	.get_mode = get_mode,
+	.set_mode = set_mode,
+	.get_trip_type = get_trip_type,
+	.get_trip_temp = get_trip_temp,
+	.get_crit_temp = get_crit_temp,
+};
+
+
+/* cooling device callback functions */
+/**********************************************************************/
+/* print maximal fan cooling state */
+static int get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+	return sprintf(buf, "1\n");
+}
+
+/* print current fan state */
+static int get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+	u8 fan;
+	if (!ec_read(bios_settings[bios_version].fanreg, &fan)) {
+		return sprintf(buf, "%d\n",
+				(fan == bios_settings[bios_version].cmd_auto));
+	}
+	return 0;
+}
+
+/* change current fan state - is overwritten when running in kernel mode */
+static int set_cur_state(struct thermal_cooling_device *cdev,
+		unsigned int state)
+{
+	if (kernelmode) {
+		printk(KERN_ERR
+			"acerhdf: cannot change fanstate in kernelmode\n");
+		return -EINVAL;
+	}
+
+	change_fanstate(state);
+	return 0;
+}
+
+/* bind fan callbacks to fan device */
+struct thermal_cooling_device_ops acerhdf_cooling_ops = {
+	.get_max_state = get_max_state,
+	.get_cur_state = get_cur_state,
+	.set_cur_state = set_cur_state,
+};
+
+
+/* kernel module init / exit functions */
+/**********************************************************************/
+/* initialize the module */
+static int __init acerhdf_init(void)
+{
+	char const *vendor;
+	char const *version;
+	char const *release;
+	char const *product;
+	int i;
+
+
+	/* get bios data */
+	vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+	version = dmi_get_system_info(DMI_BIOS_VERSION);
+	release = dmi_get_system_info(DMI_BIOS_DATE);
+	product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+
+	/* print out bios data */
+	printk(KERN_NOTICE "acerhdf: version: %s compiledate: %s %s\n",
+			VERSION, __DATE__, __TIME__);
+	printk(KERN_NOTICE "acerhdf: biosvendor:%s\n", vendor);
+	printk(KERN_NOTICE "acerhdf: biosversion:%s\n", version);
+	printk(KERN_NOTICE "acerhdf: biosrelease:%s\n", release);
+	printk(KERN_NOTICE "acerhdf: biosproduct:%s\n", product);
+
+	if (!force_bios[0]) {
+
+		/* check if vendor of the hardware is Acer */
+		if (strcmp(vendor, "Acer")) {
+			printk(KERN_ERR
+					"acerhdf: no Acer hardware found\n");
+			return -ENODEV;
+		}
+		/* check if product is a AO - Aspire One */
+		if (strncmp(product, "AO", 2)) {
+			printk(KERN_ERR
+				"acerhdf: no Aspire One hardware found\n");
+			return -ENODEV;
+		}
+	} else {
+		printk(KERN_NOTICE
+			"acerhdf: bios version: %s forced\n",
+			version);
+		printk(KERN_NOTICE
+			"acerhdf: kernelmode disabled\n");
+		printk(KERN_NOTICE
+			"acerhdf: for more information read:\n");
+		printk(KERN_NOTICE
+			"acerhdf: http://piie.net/files/acerhdf_README.txt\n");
+		version = force_bios;
+		kernelmode = 0;
+	}
+
+	/* search bios in bios settings table */
+	for (i = 0; bios_settings[i].version[0]; ++i) {
+		if (!strcmp(bios_settings[i].version, version)) {
+			bios_version = i;
+			break;
+		}
+	}
+	if (bios_version == -1) {
+		printk(KERN_ERR "acerhdf: cannot find bios version\n");
+		return -ENODEV;
+	}
+
+	/* create cooling device */
+	acerhdf_cool_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
+			&acerhdf_cooling_ops);
+	if (IS_ERR(acerhdf_cool_dev))
+		return -ENODEV;
+
+	/* create thermal zone */
+	acerhdf_thz_dev = thermal_zone_device_register("acerhdf", 2,
+			NULL, &acerhdf_device_ops);
+	if (IS_ERR(acerhdf_thz_dev))
+		return -ENODEV;
+
+	init_waitqueue_head(&wq);
+	/* start acerhdf_thread */
+	if (kernelmode) {
+		thread_id = kernel_thread(acerhdf_thread, NULL, CLONE_KERNEL);
+		if (thread_id == 0)
+			return -EIO;
+	}
+
+	return 0;
+}
+
+/* exit the module */
+static void __exit acerhdf_exit(void)
+{
+	/* unregister thermal zone */
+	if (acerhdf_thz_dev) {
+		thermal_zone_device_unregister(acerhdf_thz_dev);
+		acerhdf_thz_dev = NULL;
+	}
+	/* unregister cooling device */
+	if (acerhdf_cool_dev) {
+		thermal_cooling_device_unregister(acerhdf_cool_dev);
+		acerhdf_cool_dev = NULL;
+	}
+	/* send SIGTERM to thread */
+	if (thread_id) {
+		kill_pid(thread_pid, SIGTERM, 1);
+		wait_for_completion(&on_exit);
+	}
+}
+
+/* what are the module init/exit functions */
+module_init(acerhdf_init);
+module_exit(acerhdf_exit);

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
  2009-02-28 18:58           ` Peter Feuerer
@ 2009-03-01 14:38             ` Matthew Garrett
  2009-03-26 22:22               ` Peter Feuerer
  2009-03-01 15:27             ` Ed Tomlinson
  1 sibling, 1 reply; 18+ messages in thread
From: Matthew Garrett @ 2009-03-01 14:38 UTC (permalink / raw)
  To: Peter Feuerer; +Cc: Joe Perches, LKML

Small number of things:

*) It looks like the patch has ended up linewrapped - it probably won't 
apply as a result.

*) x86 specific drivers are mostly moving from drivers/misc to 
drivers/platform/x86.

*) It should probably have a DMI modalias to allow it to autoload on 
appropriate hardware.

*) You have user and kernel modes - it should probably also have a bios 
mode that just leaves fan control in the same state it would be if the 
driver had never been loaded, and this should be the default.

*) The thermal code has been moved from ACPI into the generic thermal 
layer for 2.6.30. If you register thermal trip points and indicate that 
you require polling you should be able to get rid of the kernel thread 
in your driver and leave that up to the kernel.

Once that's all dealt with you should submit the patch to linux-kernel 
and Cc: Len Brown who maintains drivers/platform/x86. Include a 
description and a signed-off-by: line (and an entry in the MAINTAINERS 
file) and it should get merged.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
  2009-02-28 18:58           ` Peter Feuerer
  2009-03-01 14:38             ` Matthew Garrett
@ 2009-03-01 15:27             ` Ed Tomlinson
  1 sibling, 0 replies; 18+ messages in thread
From: Ed Tomlinson @ 2009-03-01 15:27 UTC (permalink / raw)
  To: Peter Feuerer; +Cc: Joe Perches, LKML

On Saturday 28 February 2009 13:58:28 Peter Feuerer wrote:
> Hi Joe, Hi List,
>
> thank you very much for your tips!
>
> Joe Perches writes:
> > On Fri, 2009-02-27 at 19:58 +0100, Peter Feuerer wrote:
> >> What do you think about this piece of code?
> >
> > I think you should run it through lindent/checkpatch.
>
> I let the checkpatch.pl script ran over the code and corrected every single
> warning and error. The resulting code is at the end of this email.
>
> Do you (or anyobdy else on this list) have any further tips, suggestions or
> advices? If everybody is fine with the code, what will be the next steps?
>
> kind regards,
> --peter

It this now in a state that it makes sense to put it in linux next?  or should 
mm be the target?

I would like to see this in the kernel.  It is being used.  My wife's acer is 
runing a version it.  I suspect she is far from alone.

TIA
Ed Tomlinson

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Acer Aspire One fan control
  2009-02-12 21:14   ` Peter Feuerer
@ 2009-03-09 10:54     ` Maxim Levitsky
  0 siblings, 0 replies; 18+ messages in thread
From: Maxim Levitsky @ 2009-03-09 10:54 UTC (permalink / raw)
  To: Peter Feuerer; +Cc: Ralf Hildebrandt, LKML

On Thu, 2009-02-12 at 22:14 +0100, Peter Feuerer wrote:
> Ralf Hildebrandt writes:
> 
> > There's a userspace program for that. So why add a kernel module?
> 
> Because you must write to and read from io-ports and I think this should be 
> done in the kernel. I guess the userspace program you are talking about is 
> acer_ec.pl? A perl script which needs very much cpu time for a 
> functionality which can be handled in kernelspace without causing any big 
> troubles.
Not just that.
While on acer one it seems that if I kill userspace script,
fan restores to its original state (eg: running), I would also
like such driver on my main notebook, which is also acer.
It fan is also controlled via EC, but 'control' is done via hack
in acpi thermal code, every time it is updated, it sends new temperature
to EC, so I can just unload acpi thermal driver, and use own
script/driver do do same job, but I afraid that usespace driver can
hung/be killed/etc leaving me without fan at all, so this why kernel
driver is better - it is more reliable.

Best regards,
	Maxim Levitsky



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
  2009-03-01 14:38             ` Matthew Garrett
@ 2009-03-26 22:22               ` Peter Feuerer
  2009-04-01 12:44                 ` Peter Feuerer
  2009-04-04 10:51                 ` Peter Feuerer
  0 siblings, 2 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-03-26 22:22 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Joe Perches, LKML, lenb

Hi Matthew, Hi Len,

I've been improving and testing the acerhdf module the last few days and 
here's a new patch. Thank you Matthew for your hints!

Matthew Garrett writes:
> *) It looks like the patch has ended up linewrapped - it probably won't 
> apply as a result.

Hope this is fixed now, as I'm now using git diff to create the patch.

> *) x86 specific drivers are mostly moving from drivers/misc to 
> drivers/platform/x86.

Done.

> *) It should probably have a DMI modalias to allow it to autoload on 
> appropriate hardware.

Done.

> *) You have user and kernel modes - it should probably also have a bios 
> mode that just leaves fan control in the same state it would be if the 
> driver had never been loaded, and this should be the default.

It's now started in user mode by default. The bios cares in usermode about 
the fan unless a userspace program does.

> *) The thermal code has been moved from ACPI into the generic thermal 
> layer for 2.6.30. If you register thermal trip points and indicate that 
> you require polling you should be able to get rid of the kernel thread 
> in your driver and leave that up to the kernel.

Didn't find anything about polling in the drivers/thermal/ yet, but I'll 
keep on looking for that.

> Once that's all dealt with you should submit the patch to linux-kernel 
> and Cc: Len Brown who maintains drivers/platform/x86. Include a 
> description and a signed-off-by: line (and an entry in the MAINTAINERS 
> file) and it should get merged.

Here we go:

Acerhdf is a driver for Acer Aspire One netbooks. It allows to access
the temperature sensor and to control the fan. 

Signed-off-by: Peter Feuerer <peter@piie.net>

diff --git a/MAINTAINERS b/MAINTAINERS
index 5d460c9..ed823bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -189,6 +189,12 @@ M:	jes@trained-monkey.org
 L:	linux-acenic@sunsite.dk
 S:	Maintained
 
+ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
+P:	Peter Feuerer
+M:	peter@piie.net
+W:	http://piie.net/?section=acerhdf
+S:	Maintained
+
 ACER WMI LAPTOP EXTRAS
 P:	Carlos Corbacho
 M:	carlos@strangeworlds.co.uk
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 3608081..4bb04aa 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -34,6 +34,25 @@ config ACER_WMI
 	  If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
 	  here.
 
+config ACERHDF
+	tristate "Acer Aspire One temperature and fan driver"
+	depends on THERMAL
+	depends on THERMAL_HWMON
+	---help---
+	  This is a driver for Acer Aspire One netbooks. It allows to access
+	  the temperature sensor and to control the fan. 
+
+	  The driver is started in "user" mode where the Bios takes care about
+	  controlling the fan, unless a userspace program controls it.
+	  To let the kernelmodule handle the fan, do:
+	  echo kernel > /sys/class/thermal/thermal_zone0/mode
+
+	  For more information about this driver see
+	  <http://piie.net/files/acerhdf_README.txt>
+
+	  If you have an Acer Aspire One netbook, say Y or M
+	  here.
+
 config ASUS_LAPTOP
 	tristate "Asus Laptop Extras (EXPERIMENTAL)"
 	depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index e290651..01e9353 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_MSI_LAPTOP)	+= msi-laptop.o
 obj-$(CONFIG_COMPAL_LAPTOP)	+= compal-laptop.o
 obj-$(CONFIG_DELL_LAPTOP)	+= dell-laptop.o
 obj-$(CONFIG_ACER_WMI)		+= acer-wmi.o
+obj-$(CONFIG_ACERHDF)		+= acerhdf.o
 obj-$(CONFIG_HP_WMI)		+= hp-wmi.o
 obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
new file mode 100644
index 0000000..63e7f1f
--- /dev/null
+++ b/drivers/platform/x86/acerhdf.c
@@ -0,0 +1,646 @@
+/*
+ * acerhdf - A kernelmodule which monitors the temperature
+ *           of the aspire one netbook, turns on/off the fan
+ *           as soon as the upper/lower threshold is reached.
+ *
+ * (C) 2009 - Peter Feuerer     peter (a) piie.net
+ *                              http://piie.net
+ *
+ *
+ *
+ * Inspired by and many thanks to:
+ *  o acerfand   - Rachel Greenham
+ *  o acer_ec.pl - Michael Kurz     michi.kurz (at) googlemail.com
+ *               - Petr Tomasek     tomasek (#) etf,cuni,cz
+ *               - Carlos Corbacho  cathectic (at) gmail.com
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *  06-February-2009: Version 0.1:
+ *  - first relase, containing absolutely no bugs! ;)
+ *
+ *  06-February-2009: Version 0.1.1:
+ *  - found first bug :-) - it didn't check the bios vendor
+ *  - check if the bios vendor is Acer
+ *  - added bios 3301
+ *
+ *  06-February-2009: Version 0.1.2:
+ *  - added fork for deamon mode, now a real daemon is spawned
+ *  - added device vendor "INSYDE"
+ *
+ *  13-February-2009: Version 0.2:
+ *  - ported to kernelspace
+ *
+ *  19-February-2009: Version 0.2.1:
+ *  - added Bios Version 3308
+ *  - cleaned up the includes
+ *
+ *  21-February-2009: Version 0.2.2:
+ *  - changed settings for Bios 3309 as old settings caused lock ups
+ *     - thanks to Frank Reimann
+ *
+ *  21-February-2009: Version 0.2.2-2:
+ *  - added linux/sched.h to includes again, as it won't compile for
+ *    kernel < 2.6.28 without it.
+ *
+ *  23-February-2009: Version 0.3:
+ *  - tied to termal layer
+ *  - added parameters to /sys/modules/acerhdf/parameters/
+ *
+ *  25-February-2009: Version 0.3.1:
+ *  - fixed starting the module in user mode when force_bios param
+ *    is given
+ *
+ *  28-February-2009: Version 0.3.2:
+ *  - changed coding style to fit the coding style of the kernel
+ *    and checked it via checkpatch
+ *
+ *  24-March-2009: Version 0.4.0:
+ *  - added MODULE_ALIAS macro
+ *  - added Gateway and Packard Bell Bios
+ *  - added suspend / resume functionality
+ *
+ *  25-March-2009: Version 0.4.1:
+ *  - coding style
+ *  - minor bugfixes
+ *
+ *  26-March-2009: Version 0.4.2:
+ *  - replaced kernel threads by kthread api
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/dmi.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/sched.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+
+#define VERSION "0.4.2"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Feuerer");
+MODULE_DESCRIPTION("Aspire One temperature and fan driver");
+MODULE_ALIAS("dmi:*:*Acer*:*:");
+MODULE_ALIAS("dmi:*:*Gateway*:*:");
+MODULE_ALIAS("dmi:*:*Packard Bell*:*:");
+
+/* thread handling variables / funktions */
+static struct task_struct *thread;
+static struct pid *thread_pid;
+static wait_queue_head_t wq;
+static DECLARE_COMPLETION(on_exit);
+static int acerhdf_thread(void *data);
+
+/* global variables */
+static int interval = 10;
+static int fanon = 67;
+static int fanoff = 62;
+static int verbose;
+static int kernelmode = 0;
+static int fanstate = 1;
+static int bios_version = -1;
+static char force_bios[16];
+struct thermal_zone_device *acerhdf_thz_dev;
+struct thermal_cooling_device *acerhdf_cool_dev;
+
+/* module parameters */
+module_param(interval, int, 0600);
+MODULE_PARM_DESC(interval, "Polling interval of temperature check");
+module_param(fanon, int, 0600);
+MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
+module_param(fanoff, int, 0600);
+MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
+module_param(verbose, int, 0600);
+MODULE_PARM_DESC(verbose, "Enable verbose dmesg outputs");
+module_param_string(force_bios, force_bios, 16, 0);
+MODULE_PARM_DESC(force_bios, "Force bios version and omit bios check");
+
+/* bios settings */
+/**********************************************************************/
+struct bios_settings_t {
+	const char *vendor;
+	const char *version;
+	unsigned char fanreg;
+	unsigned char tempreg;
+	unsigned char cmd_off;
+	unsigned char cmd_auto;
+	unsigned char state_off;
+};
+
+/* some bios versions have different commands and
+ * maybe also different register addresses */
+static const struct bios_settings_t bios_settings[] = {
+	{"Acer", "v0.3109", 0x55, 0x58, 0x1f, 0x00, 0x1f},
+	{"Acer", "v0.3114", 0x55, 0x58, 0x1f, 0x00, 0x1f},
+	{"Acer", "v0.3301", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"Acer", "v0.3304", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"Acer", "v0.3305", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"Acer", "v0.3308", 0x55, 0x58, 0xaf, 0x00, 0xaf},
+	{"Acer", "v0.3309", 0x55, 0x58, 0x21, 0x00, 0x21},
+	{"Gateway", "v0.3103", 0x55, 0x58, 0x21, 0x00, 0x21},
+	{"Packard Bell", "v0.3105", 0x55, 0x58, 0x21, 0x00, 0x21},
+	{"", 0, 0, 0, 0, 0}
+};
+
+
+/* start kthread */
+static int acerhdf_thread_start(void)
+{
+	thread = kthread_run(acerhdf_thread, NULL, "acerhdf");
+	if (IS_ERR(thread))
+		return -1;
+	return 0;
+}
+
+
+/* acer ec functions */
+/**********************************************************************/
+/* switch on/off the fan */
+static void change_fanstate(int state)
+{
+	if (verbose)
+		printk(KERN_NOTICE "acerhdf: fan %s\n", state ? "ON" : "OFF");
+
+	ec_write(bios_settings[bios_version].fanreg,
+			state ? bios_settings[bios_version].cmd_auto :
+			bios_settings[bios_version].cmd_off);
+}
+
+/* thread to monitor the temperature and control the fan */
+static int acerhdf_thread(void *data)
+{
+	unsigned long timeout;
+	u8 temp = 0;
+	u8 last_temp = 0;
+	int unchanged_cnt = 0;
+
+	fanstate = 1;
+	daemonize("acerhdf");
+	allow_signal(SIGTERM);
+	thread_pid = task_pid(current);
+	for (;;) {
+		if (!ec_read(bios_settings[bios_version].tempreg, &temp)) {
+			/* print temperature in verbose mode */
+			if (verbose)
+				printk(KERN_NOTICE "acerhdf: Temperature is: %d\n",
+					temp);
+
+			/* if temperature is greater than fanon,
+			 * switch on fan */
+			if (temp >= fanon && fanstate == 0) {
+				change_fanstate(1);
+				fanstate = 1;
+			}
+			/* if temperature is less than fanoff,
+			 * switch off fan */
+			else if (temp < fanoff && fanstate == 1) {
+				change_fanstate(0);
+				fanstate = 0;
+			}
+		}
+
+		/* sleep interval seconds */
+		timeout = HZ*interval;
+		timeout = wait_event_interruptible_timeout(wq,
+				(timeout == 0), timeout);
+
+		/* if wait was interrupted by SIGTERM, end thread */
+		if (timeout == -ERESTARTSYS) {
+			printk(KERN_NOTICE "acerhdf: ending kernelmode\n");
+			break;
+		}
+
+		/* check if read temperature is reasonable. If not,
+		 * change to user mode and turn on fan to save the
+		 * hardware */
+		if (last_temp == temp && temp < 30) {
+			if (unchanged_cnt++ >= 10) {
+				printk(KERN_ERR
+					"acerhdf: cannot read temperature:\n");
+				printk(KERN_ERR
+					"acerhdf: switching to user mode\n");
+				kernelmode = 0;
+				break;
+			}
+		} else
+			unchanged_cnt = 0;
+
+		last_temp = temp;
+	}
+	/* turn on fan before ending the thread */
+	change_fanstate(1);
+	thread_pid = NULL;
+	complete_and_exit(&on_exit, 0);
+}
+
+/* thermal zone callback functions */
+/**********************************************************************/
+static int get_ec_temp(struct thermal_zone_device *thermal, char *buf)
+{
+	u8 temp;
+	/* return temperature */
+	if (!ec_read(bios_settings[bios_version].tempreg, &temp))
+		return sprintf(buf, "%d\n", temp);
+
+	return -EINVAL;
+}
+
+/* bind the cooling device to the thermal zone */
+static int bind(struct thermal_zone_device *thermal,
+		struct thermal_cooling_device *cdev)
+{
+	/* if the cooling device is the one from acerhdf bind it */
+	if (cdev == acerhdf_cool_dev) {
+		if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error binding cooling dev\n");
+			return -EINVAL;
+		}
+		if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error binding cooling dev\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* unbind cooling device from thermal zone */
+static int unbind(struct thermal_zone_device *thermal,
+		struct thermal_cooling_device *cdev)
+{
+	if (cdev == acerhdf_cool_dev) {
+		if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error unbinding cooling dev\n");
+			return -EINVAL;
+		}
+		if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
+			printk(KERN_ERR
+				"acerhdf: error unbinding cooling dev\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* print currend operation mode - kernel / user */
+static int get_mode(struct thermal_zone_device *thermal,
+		char *buf)
+{
+	if (!kernelmode)
+		return sprintf(buf, "user\n");
+
+	else
+		return sprintf(buf, "kernel\n");
+
+	return 0;
+}
+
+/* set operation mode;
+ * kernel: a kernel thread takes care about managing the
+ *	 fan (see acerhdf_thread)
+ * user: kernel thread is stopped and a userspace tool
+ *	 should take care about managing the fan
+ */
+static int set_mode(struct thermal_zone_device *thermal,
+		const char *buf)
+{
+	/* set mode to user mode */
+	if (!strncmp(buf, "user", 4)) {
+		if (verbose)
+			printk(KERN_NOTICE "acerhdf: set to usermode\n");
+
+		/* send SIGTERM to thread and wait until thread died */
+		if (thread_pid)
+			kill_pid(thread_pid, SIGTERM, 1);
+
+		wait_for_completion(&on_exit);
+		kernelmode = 0;
+		return 0;
+	}
+	/* set to kernel mode */
+	else if (!strncmp(buf, "kernel", 6)) {
+		/* start acerhdf_thread */
+		if (!kernelmode) {
+			if (acerhdf_thread_start())
+				return -EIO;
+		}
+
+		if (verbose)
+			printk(KERN_NOTICE "acerhdf: set to kernelmode\n");
+
+		kernelmode = 1;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+/* print the name of the trip point */
+static int get_trip_type(struct thermal_zone_device *thermal,
+		int trip, char *buf)
+{
+	if (trip == 0)
+		return sprintf(buf, "fanoff\n");
+
+	else if (trip == 1)
+		return sprintf(buf, "fanon\n");
+
+	return 0;
+}
+
+/* print the temperature at which the trip point gets active */
+static int get_trip_temp(struct thermal_zone_device *thermal,
+		int trip, char *buf)
+{
+	if (trip == 0)
+		return sprintf(buf, "%d\n", fanoff);
+
+	else if (trip == 1)
+		return sprintf(buf, "%d\n", fanon);
+
+	return 0;
+}
+
+static int get_crit_temp(struct thermal_zone_device *thermal,
+		unsigned long *temperature)
+{
+	return 0;
+}
+
+/* bind callback functions to thermalzone */
+struct thermal_zone_device_ops acerhdf_device_ops = {
+	.bind = bind,
+	.unbind = unbind,
+	.get_temp = get_ec_temp,
+	.get_mode = get_mode,
+	.set_mode = set_mode,
+	.get_trip_type = get_trip_type,
+	.get_trip_temp = get_trip_temp,
+	.get_crit_temp = get_crit_temp,
+};
+
+
+/* cooling device callback functions */
+/**********************************************************************/
+/* print maximal fan cooling state */
+static int get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+	return sprintf(buf, "1\n");
+}
+
+/* print current fan state */
+static int get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+	u8 fan;
+	if (!ec_read(bios_settings[bios_version].fanreg, &fan)) {
+		return sprintf(buf, "%d\n",
+				(fan == bios_settings[bios_version].cmd_auto));
+	}
+	return 0;
+}
+
+/* change current fan state - is overwritten when running in kernel mode */
+static int set_cur_state(struct thermal_cooling_device *cdev,
+		unsigned int state)
+{
+	if (kernelmode) {
+		printk(KERN_ERR
+			"acerhdf: cannot change fanstate in kernelmode\n");
+		return -EINVAL;
+	}
+
+	change_fanstate(state);
+	return 0;
+}
+
+/* bind fan callbacks to fan device */
+struct thermal_cooling_device_ops acerhdf_cooling_ops = {
+	.get_max_state = get_max_state,
+	.get_cur_state = get_cur_state,
+	.set_cur_state = set_cur_state,
+};
+
+/* platform callbacks */
+/**********************************************************************/
+/* go suspend */
+static int acerhdf_suspend(struct platform_device *dev,
+		pm_message_t state)
+{
+	if (verbose)
+		printk(KERN_NOTICE "acerhdf: going suspend\n");
+	/* send SIGTERM to thread and wait until thread died */
+	if (thread_pid)
+		kill_pid(thread_pid, SIGTERM, 1);
+
+	wait_for_completion(&on_exit);
+	return 0;
+}
+
+/* wake up */
+static int acerhdf_resume(struct platform_device *device)
+{
+	if (verbose)
+		printk(KERN_NOTICE "acerhdf: resuming\n");
+	if (kernelmode) {
+		if (acerhdf_thread_start())
+			return -EIO;
+	}
+	return 0;
+}
+
+/* platform probe */
+static int __devinit acerhdf_probe(struct platform_device *device)
+{
+	return 0;
+}
+
+static int acerhdf_remove(struct platform_device *device)
+{
+	return 0;
+}
+
+static struct platform_driver acerhdf_driver = {
+	.driver = {
+		.name = "acerhdf",
+		.owner = THIS_MODULE,
+	},
+	.probe = acerhdf_probe,
+	.remove = acerhdf_remove,
+	.suspend = acerhdf_suspend,
+	.resume = acerhdf_resume,
+};
+
+static struct platform_device *acerhdf_device;
+
+/* kernel module init / exit functions */
+/**********************************************************************/
+/* initialize the module */
+static int __init acerhdf_init(void)
+{
+	char const *vendor;
+	char const *version;
+	char const *release;
+	char const *product;
+	int i;
+	int ret_val = 0;
+
+
+	/* get bios data */
+	vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+	version = dmi_get_system_info(DMI_BIOS_VERSION);
+	release = dmi_get_system_info(DMI_BIOS_DATE);
+	product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+
+	/* print out bios data */
+	printk(KERN_NOTICE "acerhdf: version: %s compilation date: %s %s\n",
+			VERSION, __DATE__, __TIME__);
+	printk(KERN_NOTICE "acerhdf: biosvendor:%s\n", vendor);
+	printk(KERN_NOTICE "acerhdf: biosversion:%s\n", version);
+	printk(KERN_NOTICE "acerhdf: biosrelease:%s\n", release);
+	printk(KERN_NOTICE "acerhdf: biosproduct:%s\n", product);
+
+	if (!force_bios[0]) {
+		/* check if product is a AO - Aspire One */
+		if (strncmp(product, "AO", 2)) {
+			printk(KERN_ERR
+				"acerhdf: no Aspire One hardware found\n");
+			ret_val = -ENODEV;
+			goto EXIT;
+		}
+	} else {
+		printk(KERN_NOTICE
+			"acerhdf: bios version: %s forced\n",
+			version);
+		printk(KERN_NOTICE
+			"acerhdf: kernelmode disabled\n");
+		printk(KERN_NOTICE
+			"acerhdf: for more information read:\n");
+		printk(KERN_NOTICE
+			"acerhdf: http://piie.net/files/acerhdf_README.txt\n");
+		version = force_bios;
+		kernelmode = 0;
+	}
+
+	/* search bios and bios vendor in bios settings table */
+	for (i = 0; bios_settings[i].version[0]; ++i) {
+		if (!strcmp(bios_settings[i].vendor, vendor) &&
+				!strcmp(bios_settings[i].version, version)) {
+			bios_version = i;
+			break;
+		}
+	}
+	if (bios_version == -1) {
+		printk(KERN_ERR "acerhdf: cannot find bios version\n");
+		ret_val = -ENODEV;
+		goto EXIT;
+	}
+
+	/* register platform device */
+	if (platform_driver_register(&acerhdf_driver)) {
+		ret_val = -ENODEV;
+		goto EXIT;
+	}
+	acerhdf_device = platform_device_alloc("acerhdf", -1);
+	platform_device_add(acerhdf_device);
+
+	/* create cooling device */
+	acerhdf_cool_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
+			&acerhdf_cooling_ops);
+	if (IS_ERR(acerhdf_cool_dev)) {
+		ret_val = -ENODEV;
+		goto EXIT_PLAT_UNREG;
+	}
+
+	/* create thermal zone */
+	acerhdf_thz_dev = thermal_zone_device_register("acerhdf", 2,
+			NULL, &acerhdf_device_ops);
+	if (IS_ERR(acerhdf_thz_dev)) {
+		ret_val = -ENODEV;
+		goto EXIT_COOL_UNREG;
+	}
+
+	init_waitqueue_head(&wq);
+	/* start acerhdf_thread */
+	if (kernelmode) {
+		if (acerhdf_thread_start()) {
+			ret_val = -EIO;
+			goto EXIT_THERM_UNREG;
+		}
+	}
+	goto EXIT;
+
+EXIT_THERM_UNREG:
+	/* unregister thermal zone */
+	if (acerhdf_thz_dev) {
+		thermal_zone_device_unregister(acerhdf_thz_dev);
+		acerhdf_thz_dev = NULL;
+	}
+
+EXIT_COOL_UNREG:
+	/* unregister cooling device */
+	if (acerhdf_cool_dev) {
+		thermal_cooling_device_unregister(acerhdf_cool_dev);
+		acerhdf_cool_dev = NULL;
+	}
+
+EXIT_PLAT_UNREG:
+	/* unregister platform device */
+	if (acerhdf_device) {
+		platform_device_del(acerhdf_device);
+		platform_driver_unregister(&acerhdf_driver);
+	}
+
+EXIT:
+	return ret_val;
+}
+
+/* exit the module */
+static void __exit acerhdf_exit(void)
+{
+	/* unregister cooling device */
+	if (acerhdf_cool_dev) {
+		thermal_cooling_device_unregister(acerhdf_cool_dev);
+		acerhdf_cool_dev = NULL;
+	}
+	/* unregister thermal zone */
+	if (acerhdf_thz_dev) {
+		thermal_zone_device_unregister(acerhdf_thz_dev);
+		acerhdf_thz_dev = NULL;
+	}
+	/* send SIGTERM to thread */
+	if (thread_pid) {
+		kill_pid(thread_pid, SIGTERM, 1);
+		wait_for_completion(&on_exit);
+	}
+
+	/* unregister platform device */
+	if (acerhdf_device) {
+		platform_device_del(acerhdf_device);
+		platform_driver_unregister(&acerhdf_driver);
+	}
+}
+
+/* what are the module init/exit functions */
+module_init(acerhdf_init);
+module_exit(acerhdf_exit);

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
  2009-03-26 22:22               ` Peter Feuerer
@ 2009-04-01 12:44                 ` Peter Feuerer
  2009-04-12 20:49                   ` Peter Feuerer
  2009-04-04 10:51                 ` Peter Feuerer
  1 sibling, 1 reply; 18+ messages in thread
From: Peter Feuerer @ 2009-04-01 12:44 UTC (permalink / raw)
  To: lenb; +Cc: LKML

Hi Len,

have you already had a look at the patch? Is there anything what should be 
done in another way before you're going to apply it? 
Just for information, the kernelmodule has been downloaded from my homepage 
about 2200 times in march. So there are a lot of people already using it.

kind regards,
--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
  2009-03-26 22:22               ` Peter Feuerer
  2009-04-01 12:44                 ` Peter Feuerer
@ 2009-04-04 10:51                 ` Peter Feuerer
  1 sibling, 0 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-04-04 10:51 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Joe Perches, LKML, lenb

Hi Matthew,

Len didn't respond to my email with my patch yet. Did I do anything wrong? 
Or is it just normal that it takes some weeks until he replies?

thank you very much,

kind regards,
--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] acerhdf: Acer Aspire One fan control
  2009-04-01 12:44                 ` Peter Feuerer
@ 2009-04-12 20:49                   ` Peter Feuerer
  0 siblings, 0 replies; 18+ messages in thread
From: Peter Feuerer @ 2009-04-12 20:49 UTC (permalink / raw)
  To: lenb; +Cc: LKML

Hi len,

could you please write at least a short reply to one of my mails? It feels 
like you are ignoring me. I mean, if you don't have time to investigate the 
kernelmodule or if you don't want to have it in the kernel, I'm fine with 
it. But please reply. Being ignored makes me feel sad...

regards and looking forward to reading a reply,
--peter

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2009-04-12 20:49 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-02-12 20:37 Acer Aspire One fan control Peter Feuerer
2009-02-12 21:03 ` Ralf Hildebrandt
2009-02-12 21:14   ` Peter Feuerer
2009-03-09 10:54     ` Maxim Levitsky
2009-02-13 10:35 ` Thomas Renninger
2009-02-13 19:34   ` Peter Feuerer
2009-02-13 13:40 ` Matthew Garrett
2009-02-13 18:59   ` Peter Feuerer
2009-02-13 19:18     ` Matthew Garrett
2009-02-13 19:45       ` Peter Feuerer
2009-02-27 18:58       ` [PATCH] acerhdf: " Peter Feuerer
     [not found]         ` <1235763653.24506.6.camel@localhost>
2009-02-28 18:58           ` Peter Feuerer
2009-03-01 14:38             ` Matthew Garrett
2009-03-26 22:22               ` Peter Feuerer
2009-04-01 12:44                 ` Peter Feuerer
2009-04-12 20:49                   ` Peter Feuerer
2009-04-04 10:51                 ` Peter Feuerer
2009-03-01 15:27             ` Ed Tomlinson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.