2007-10-31 13:46:41 +03:00
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/leds.h>
# include <linux/io.h>
# include <linux/dmi.h>
# include <linux/i8042.h>
# define CLEVO_MAIL_LED_OFF 0x0084
# define CLEVO_MAIL_LED_BLINK_1HZ 0x008A
# define CLEVO_MAIL_LED_BLINK_0_5HZ 0x0083
2008-03-09 23:59:57 +03:00
MODULE_AUTHOR ( " Márton Németh <nm127@freemail.hu> " ) ;
2007-10-31 13:46:41 +03:00
MODULE_DESCRIPTION ( " Clevo mail LED driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static unsigned int __initdata nodetect ;
module_param_named ( nodetect , nodetect , bool , 0 ) ;
MODULE_PARM_DESC ( nodetect , " Skip DMI hardware detection " ) ;
static struct platform_device * pdev ;
static int __init clevo_mail_led_dmi_callback ( const struct dmi_system_id * id )
{
printk ( KERN_INFO KBUILD_MODNAME " : '%s' found \n " , id - > ident ) ;
return 1 ;
}
/*
* struct mail_led_whitelist - List of known good models
*
* Contains the known good models this driver is compatible with .
* When adding a new model try to be as strict as possible . This
* makes it possible to keep the false positives ( the model is
* detected as working , but in reality it is not ) as low as
* possible .
*/
static struct dmi_system_id __initdata mail_led_whitelist [ ] = {
{
. callback = clevo_mail_led_dmi_callback ,
. ident = " Clevo D410J " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " VIA " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " K8N800 " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " VT8204B " )
}
} ,
{
. callback = clevo_mail_led_dmi_callback ,
. ident = " Clevo M5x0N " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " CLEVO Co. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " M5x0N " )
}
} ,
{
. callback = clevo_mail_led_dmi_callback ,
. ident = " Positivo Mobile " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " CLEVO Co. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " M5X0V " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Positivo Mobile " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " VT6198 " )
}
} ,
2008-03-09 23:47:59 +03:00
{
. callback = clevo_mail_led_dmi_callback ,
. ident = " Clevo D400P " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " Clevo " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " D400P " ) ,
DMI_MATCH ( DMI_BOARD_VERSION , " Rev.A " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " 0106 " )
}
} ,
2007-10-31 13:46:41 +03:00
{
. callback = clevo_mail_led_dmi_callback ,
. ident = " Clevo D410V " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " Clevo, Co. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " D400V/D470V " ) ,
DMI_MATCH ( DMI_BOARD_VERSION , " SS78B " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " Rev. A1 " )
}
} ,
{ }
} ;
static void clevo_mail_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
2009-09-16 12:06:43 +04:00
i8042_lock_chip ( ) ;
2007-10-31 13:46:41 +03:00
if ( value = = LED_OFF )
i8042_command ( NULL , CLEVO_MAIL_LED_OFF ) ;
else if ( value < = LED_HALF )
i8042_command ( NULL , CLEVO_MAIL_LED_BLINK_0_5HZ ) ;
else
i8042_command ( NULL , CLEVO_MAIL_LED_BLINK_1HZ ) ;
2009-09-16 12:06:43 +04:00
i8042_unlock_chip ( ) ;
2007-10-31 13:46:41 +03:00
}
2007-10-31 17:09:05 +03:00
static int clevo_mail_led_blink ( struct led_classdev * led_cdev ,
2008-03-09 23:59:57 +03:00
unsigned long * delay_on ,
unsigned long * delay_off )
2007-10-31 17:09:05 +03:00
{
int status = - EINVAL ;
2009-09-16 12:06:43 +04:00
i8042_lock_chip ( ) ;
2007-10-31 17:09:05 +03:00
if ( * delay_on = = 0 /* ms */ & & * delay_off = = 0 /* ms */ ) {
/* Special case: the leds subsystem requested us to
* chose one user friendly blinking of the LED , and
* start it . Let ' s blink the led slowly ( 0.5 Hz ) .
*/
* delay_on = 1000 ; /* ms */
* delay_off = 1000 ; /* ms */
i8042_command ( NULL , CLEVO_MAIL_LED_BLINK_0_5HZ ) ;
status = 0 ;
} else if ( * delay_on = = 500 /* ms */ & & * delay_off = = 500 /* ms */ ) {
/* blink the led with 1Hz */
i8042_command ( NULL , CLEVO_MAIL_LED_BLINK_1HZ ) ;
status = 0 ;
} else if ( * delay_on = = 1000 /* ms */ & & * delay_off = = 1000 /* ms */ ) {
/* blink the led with 0.5Hz */
i8042_command ( NULL , CLEVO_MAIL_LED_BLINK_0_5HZ ) ;
status = 0 ;
} else {
printk ( KERN_DEBUG KBUILD_MODNAME
" : clevo_mail_led_blink(..., %lu, %lu), "
" returning -EINVAL (unsupported) \n " ,
* delay_on , * delay_off ) ;
}
2009-09-16 12:06:43 +04:00
i8042_unlock_chip ( ) ;
2007-10-31 17:09:05 +03:00
return status ;
}
2007-10-31 13:46:41 +03:00
static struct led_classdev clevo_mail_led = {
2007-10-31 17:00:07 +03:00
. name = " clevo::mail " ,
2007-10-31 13:46:41 +03:00
. brightness_set = clevo_mail_led_set ,
2007-10-31 17:09:05 +03:00
. blink_set = clevo_mail_led_blink ,
2009-01-08 20:55:03 +03:00
. flags = LED_CORE_SUSPENDRESUME ,
2007-10-31 13:46:41 +03:00
} ;
2009-07-12 00:52:34 +04:00
static int __devinit clevo_mail_led_probe ( struct platform_device * pdev )
2007-10-31 13:46:41 +03:00
{
return led_classdev_register ( & pdev - > dev , & clevo_mail_led ) ;
}
static int clevo_mail_led_remove ( struct platform_device * pdev )
{
led_classdev_unregister ( & clevo_mail_led ) ;
return 0 ;
}
static struct platform_driver clevo_mail_led_driver = {
. probe = clevo_mail_led_probe ,
. remove = clevo_mail_led_remove ,
. driver = {
. name = KBUILD_MODNAME ,
2008-04-16 01:34:30 +04:00
. owner = THIS_MODULE ,
2007-10-31 13:46:41 +03:00
} ,
} ;
static int __init clevo_mail_led_init ( void )
{
int error = 0 ;
int count = 0 ;
/* Check with the help of DMI if we are running on supported hardware */
if ( ! nodetect ) {
count = dmi_check_system ( mail_led_whitelist ) ;
} else {
count = 1 ;
printk ( KERN_ERR KBUILD_MODNAME " : Skipping DMI detection. "
" If the driver works on your hardware please "
" report model and the output of dmidecode in tracker "
" at http://sourceforge.net/projects/clevo-mailled/ \n " ) ;
}
if ( ! count )
return - ENODEV ;
pdev = platform_device_register_simple ( KBUILD_MODNAME , - 1 , NULL , 0 ) ;
if ( ! IS_ERR ( pdev ) ) {
error = platform_driver_probe ( & clevo_mail_led_driver ,
clevo_mail_led_probe ) ;
if ( error ) {
printk ( KERN_ERR KBUILD_MODNAME
" : Can't probe platform driver \n " ) ;
platform_device_unregister ( pdev ) ;
}
} else
error = PTR_ERR ( pdev ) ;
return error ;
}
static void __exit clevo_mail_led_exit ( void )
{
platform_device_unregister ( pdev ) ;
platform_driver_unregister ( & clevo_mail_led_driver ) ;
clevo_mail_led_set ( NULL , LED_OFF ) ;
}
module_init ( clevo_mail_led_init ) ;
module_exit ( clevo_mail_led_exit ) ;