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        .flags                  = LED_CORE_SUSPENDRESUME,
 146};
 147
 148static int __init clevo_mail_led_probe(struct platform_device *pdev)
 149{
 150        return led_classdev_register(&pdev->dev, &clevo_mail_led);
 151}
 152
 153static int clevo_mail_led_remove(struct platform_device *pdev)
 154{
 155        led_classdev_unregister(&clevo_mail_led);
 156        return 0;
 157}
 158
 159static struct platform_driver clevo_mail_led_driver = {
 160        .probe          = clevo_mail_led_probe,
 161        .remove         = clevo_mail_led_remove,
 162        .driver         = {
 163                .name           = KBUILD_MODNAME,
 164                .owner          = THIS_MODULE,
 165        },
 166};
 167
 168static int __init clevo_mail_led_init(void)
 169{
 170        int error = 0;
 171        int count = 0;
 172
 173        /* Check with the help of DMI if we are running on supported hardware */
 174        if (!nodetect) {
 175                count = dmi_check_system(mail_led_whitelist);
 176        } else {
 177                count = 1;
 178                printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. "
 179                       "If the driver works on your hardware please "
 180                       "report model and the output of dmidecode in tracker "
 181                       "at http://sourceforge.net/projects/clevo-mailled/\n");
 182        }
 183
 184        if (!count)
 185                return -ENODEV;
 186
 187        pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
 188        if (!IS_ERR(pdev)) {
 189                error = platform_driver_probe(&clevo_mail_led_driver,
 190                                              clevo_mail_led_probe);
 191                if (error) {
 192                        printk(KERN_ERR KBUILD_MODNAME
 193                               ": Can't probe platform driver\n");
 194                        platform_device_unregister(pdev);
 195                }
 196        } else
 197                error = PTR_ERR(pdev);
 198
 199        return error;
 200}
 201
 202static void __exit clevo_mail_led_exit(void)
 203{
 204        platform_device_unregister(pdev);
 205        platform_driver_unregister(&clevo_mail_led_driver);
 206
 207        clevo_mail_led_set(NULL, LED_OFF);
 208}
 209
 210module_init(clevo_mail_led_init);
 211module_exit(clevo_mail_led_exit);
 212