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 bool __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        i8042_lock_chip();
  97
  98        if (value == LED_OFF)
  99                i8042_command(NULL, CLEVO_MAIL_LED_OFF);
 100        else if (value <= LED_HALF)
 101                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
 102        else
 103                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
 104
 105        i8042_unlock_chip();
 106
 107}
 108
 109static int clevo_mail_led_blink(struct led_classdev *led_cdev,
 110                                unsigned long *delay_on,
 111                                unsigned long *delay_off)
 112{
 113        int status = -EINVAL;
 114
 115        i8042_lock_chip();
 116
 117        if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
 118                /* Special case: the leds subsystem requested us to
 119                 * chose one user friendly blinking of the LED, and
 120                 * start it. Let's blink the led slowly (0.5Hz).
 121                 */
 122                *delay_on = 1000; /* ms */
 123                *delay_off = 1000; /* ms */
 124                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
 125                status = 0;
 126
 127        } else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
 128                /* blink the led with 1Hz */
 129                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
 130                status = 0;
 131
 132        } else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
 133                /* blink the led with 0.5Hz */
 134                i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
 135                status = 0;
 136
 137        } else {
 138                printk(KERN_DEBUG KBUILD_MODNAME
 139                       ": clevo_mail_led_blink(..., %lu, %lu),"
 140                       " returning -EINVAL (unsupported)\n",
 141                       *delay_on, *delay_off);
 142        }
 143
 144        i8042_unlock_chip();
 145
 146        return status;
 147}
 148
 149static struct led_classdev clevo_mail_led = {
 150        .name                   = "clevo::mail",
 151        .brightness_set         = clevo_mail_led_set,
 152        .blink_set              = clevo_mail_led_blink,
 153        .flags                  = LED_CORE_SUSPENDRESUME,
 154};
 155
 156static int __devinit clevo_mail_led_probe(struct platform_device *pdev)
 157{
 158        return led_classdev_register(&pdev->dev, &clevo_mail_led);
 159}
 160
 161static int clevo_mail_led_remove(struct platform_device *pdev)
 162{
 163        led_classdev_unregister(&clevo_mail_led);
 164        return 0;
 165}
 166
 167static struct platform_driver clevo_mail_led_driver = {
 168        .probe          = clevo_mail_led_probe,
 169        .remove         = clevo_mail_led_remove,
 170        .driver         = {
 171                .name           = KBUILD_MODNAME,
 172                .owner          = THIS_MODULE,
 173        },
 174};
 175
 176static int __init clevo_mail_led_init(void)
 177{
 178        int error = 0;
 179        int count = 0;
 180
 181        /* Check with the help of DMI if we are running on supported hardware */
 182        if (!nodetect) {
 183                count = dmi_check_system(mail_led_whitelist);
 184        } else {
 185                count = 1;
 186                printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. "
 187                       "If the driver works on your hardware please "
 188                       "report model and the output of dmidecode in tracker "
 189                       "at http://sourceforge.net/projects/clevo-mailled/\n");
 190        }
 191
 192        if (!count)
 193                return -ENODEV;
 194
 195        pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
 196        if (!IS_ERR(pdev)) {
 197                error = platform_driver_probe(&clevo_mail_led_driver,
 198                                              clevo_mail_led_probe);
 199                if (error) {
 200                        printk(KERN_ERR KBUILD_MODNAME
 201                               ": Can't probe platform driver\n");
 202                        platform_device_unregister(pdev);
 203                }
 204        } else
 205                error = PTR_ERR(pdev);
 206
 207        return error;
 208}
 209
 210static void __exit clevo_mail_led_exit(void)
 211{
 212        platform_device_unregister(pdev);
 213        platform_driver_unregister(&clevo_mail_led_driver);
 214
 215        clevo_mail_led_set(NULL, LED_OFF);
 216}
 217
 218module_init(clevo_mail_led_init);
 219module_exit(clevo_mail_led_exit);
 220