linux/drivers/leds/leds-clevo-mail.c
<<
>>
Prefs
   1
   2#include <linux/module.h>
   3
   4#include <linux/platform_device.h>
   5#include <linux/err.h>
   6#include <linux/leds.h>
   7
   8#include <linux/io.h>
   9#include <linux/dmi.h>
  10
  11#include <linux/i8042.h>
  12
  13#define CLEVO_MAIL_LED_OFF              0x0084
  14#define CLEVO_MAIL_LED_BLINK_1HZ        0x008A
  15#define CLEVO_MAIL_LED_BLINK_0_5HZ      0x0083
  16
  17MODULE_AUTHOR("M\xC3\xA1rton N\xC3\xA9meth <nm127@freemail.hu>");
  18MODULE_DESCRIPTION("Clevo mail LED driver");
  19MODULE_LICENSE("GPL");
  20
  21static unsigned int __initdata nodetect;
  22module_param_named(nodetect, nodetect, bool, 0);
  23MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
  24
  25static struct platform_device *pdev;
  26
  27static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
  28{
  29        printk(KERN_INFO KBUILD_MODNAME ": '%s' found\n", id->ident);
  30        return 1;
  31}
  32
  33/*
  34 * struct mail_led_whitelist - List of known good models
  35 *
  36 * Contains the known good models this driver is compatible with.
  37 * When adding a new model try to be as strict as possible. This
  38 * makes it possible to keep the false positives (the model is
  39 * detected as working, but in reality it is not) as low as
  40 * possible.
  41 */
  42static struct dmi_system_id __initdata mail_led_whitelist[] = {
  43        {
  44                .callback = clevo_mail_led_dmi_callback,
  45                .ident = "Clevo D410J",
  46                .matches = {
  47                        DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
  48                        DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
  49                        DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
  50                }
  51        },
  52        {
  53                .callback = clevo_mail_led_dmi_callback,
  54                .ident = "Clevo M5x0N",
  55                .matches = {
  56                        DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
  57                        DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
  58                }
  59        },
  60        {
  61                .callback = clevo_mail_led_dmi_callback,
  62                .ident = "Positivo Mobile",
  63                .matches = {
  64                        DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
  65                        DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
  66                        DMI_MATCH(DMI_PRODUCT_NAME, "Positivo Mobile"),
  67                        DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")
  68                }
  69        },
  70        {
  71                .callback = clevo_mail_led_dmi_callback,
  72                .ident = "Clevo D400P",
  73                .matches = {
  74                        DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),
  75                        DMI_MATCH(DMI_BOARD_NAME, "D400P"),
  76                        DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),
  77                        DMI_MATCH(DMI_PRODUCT_VERSION, "0106")
  78                }
  79        },
  80        {
  81                .callback = clevo_mail_led_dmi_callback,
  82                .ident = "Clevo D410V",
  83                .matches = {
  84                        DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),
  85                        DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),
  86                        DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),
  87                        DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")
  88                }
  89        },
  90        { }
  91};
  92
  93static void clevo_mail_led_set(struct led_classdev *led_cdev,
  94                                enum led_brightness value)
  95{
  96        if (value == LED_OFF)
  97                i8042_command(NULL, CLEVO_MAIL_LED_OFF);
  98        else if (value <= LED_HALF)
  99                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
 100        else
 101                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
 102
 103}
 104
 105static int clevo_mail_led_blink(struct led_classdev *led_cdev,
 106                                unsigned long *delay_on,
 107                                unsigned long *delay_off)
 108{
 109        int status = -EINVAL;
 110
 111        if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
 112                /* Special case: the leds subsystem requested us to
 113                 * chose one user friendly blinking of the LED, and
 114                 * start it. Let's blink the led slowly (0.5Hz).
 115                 */
 116                *delay_on = 1000; /* ms */
 117                *delay_off = 1000; /* ms */
 118                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
 119                status = 0;
 120
 121        } else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
 122                /* blink the led with 1Hz */
 123                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
 124                status = 0;
 125
 126        } else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
 127                /* blink the led with 0.5Hz */
 128                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
 129                status = 0;
 130
 131        } else {
 132                printk(KERN_DEBUG KBUILD_MODNAME
 133                       ": clevo_mail_led_blink(..., %lu, %lu),"
 134                       " returning -EINVAL (unsupported)\n",
 135                       *delay_on, *delay_off);
 136        }
 137
 138        return status;
 139}
 140
 141static struct led_classdev clevo_mail_led = {
 142        .name                   = "clevo::mail",
 143        .brightness_set         = clevo_mail_led_set,
 144        .blink_set              = clevo_mail_led_blink,
 145};
 146
 147static int __init clevo_mail_led_probe(struct platform_device *pdev)
 148{
 149        return led_classdev_register(&pdev->dev, &clevo_mail_led);
 150}
 151
 152static int clevo_mail_led_remove(struct platform_device *pdev)
 153{
 154        led_classdev_unregister(&clevo_mail_led);
 155        return 0;
 156}
 157
 158#ifdef CONFIG_PM
 159static int clevo_mail_led_suspend(struct platform_device *dev,
 160                                  pm_message_t state)
 161{
 162        led_classdev_suspend(&clevo_mail_led);
 163        return 0;
 164}
 165
 166static int clevo_mail_led_resume(struct platform_device *dev)
 167{
 168        led_classdev_resume(&clevo_mail_led);
 169        return 0;
 170}
 171#else
 172#define clevo_mail_led_suspend    NULL
 173#define clevo_mail_led_resume     NULL
 174#endif
 175
 176static struct platform_driver clevo_mail_led_driver = {
 177        .probe          = clevo_mail_led_probe,
 178        .remove         = clevo_mail_led_remove,
 179        .suspend        = clevo_mail_led_suspend,
 180        .resume         = clevo_mail_led_resume,
 181        .driver         = {
 182                .name           = KBUILD_MODNAME,
 183                .owner          = THIS_MODULE,
 184        },
 185};
 186
 187static int __init clevo_mail_led_init(void)
 188{
 189        int error = 0;
 190        int count = 0;
 191
 192        /* Check with the help of DMI if we are running on supported hardware */
 193        if (!nodetect) {
 194                count = dmi_check_system(mail_led_whitelist);
 195        } else {
 196                count = 1;
 197                printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. "
 198                       "If the driver works on your hardware please "
 199                       "report model and the output of dmidecode in tracker "
 200                       "at http://sourceforge.net/projects/clevo-mailled/\n");
 201        }
 202
 203        if (!count)
 204                return -ENODEV;
 205
 206        pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
 207        if (!IS_ERR(pdev)) {
 208                error = platform_driver_probe(&clevo_mail_led_driver,
 209                                              clevo_mail_led_probe);
 210                if (error) {
 211                        printk(KERN_ERR KBUILD_MODNAME
 212                               ": Can't probe platform driver\n");
 213                        platform_device_unregister(pdev);
 214                }
 215        } else
 216                error = PTR_ERR(pdev);
 217
 218        return error;
 219}
 220
 221static void __exit clevo_mail_led_exit(void)
 222{
 223        platform_device_unregister(pdev);
 224        platform_driver_unregister(&clevo_mail_led_driver);
 225
 226        clevo_mail_led_set(NULL, LED_OFF);
 227}
 228
 229module_init(clevo_mail_led_init);
 230module_exit(clevo_mail_led_exit);
 231