Root/drivers/leds/leds-clevo-mail.c

1#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2
3#include <linux/module.h>
4
5#include <linux/platform_device.h>
6#include <linux/err.h>
7#include <linux/leds.h>
8
9#include <linux/io.h>
10#include <linux/dmi.h>
11
12#include <linux/i8042.h>
13
14#define CLEVO_MAIL_LED_OFF 0x0084
15#define CLEVO_MAIL_LED_BLINK_1HZ 0x008A
16#define CLEVO_MAIL_LED_BLINK_0_5HZ 0x0083
17
18MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
19MODULE_DESCRIPTION("Clevo mail LED driver");
20MODULE_LICENSE("GPL");
21
22static bool __initdata nodetect;
23module_param_named(nodetect, nodetect, bool, 0);
24MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
25
26static struct platform_device *pdev;
27
28static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
29{
30    pr_info("'%s' found\n", id->ident);
31    return 1;
32}
33
34/*
35 * struct clevo_mail_led_dmi_table - List of known good models
36 *
37 * Contains the known good models this driver is compatible with.
38 * When adding a new model try to be as strict as possible. This
39 * makes it possible to keep the false positives (the model is
40 * detected as working, but in reality it is not) as low as
41 * possible.
42 */
43static struct dmi_system_id __initdata clevo_mail_led_dmi_table[] = {
44    {
45        .callback = clevo_mail_led_dmi_callback,
46        .ident = "Clevo D410J",
47        .matches = {
48            DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
49            DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
50            DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
51        }
52    },
53    {
54        .callback = clevo_mail_led_dmi_callback,
55        .ident = "Clevo M5x0N",
56        .matches = {
57            DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
58            DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
59        }
60    },
61    {
62        .callback = clevo_mail_led_dmi_callback,
63        .ident = "Clevo M5x0V",
64        .matches = {
65            DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
66            DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
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};
92MODULE_DEVICE_TABLE(dmi, clevo_mail_led_dmi_table);
93
94static void clevo_mail_led_set(struct led_classdev *led_cdev,
95                enum led_brightness value)
96{
97    i8042_lock_chip();
98
99    if (value == LED_OFF)
100        i8042_command(NULL, CLEVO_MAIL_LED_OFF);
101    else if (value <= LED_HALF)
102        i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
103    else
104        i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
105
106    i8042_unlock_chip();
107
108}
109
110static int clevo_mail_led_blink(struct led_classdev *led_cdev,
111                unsigned long *delay_on,
112                unsigned long *delay_off)
113{
114    int status = -EINVAL;
115
116    i8042_lock_chip();
117
118    if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
119        /* Special case: the leds subsystem requested us to
120         * chose one user friendly blinking of the LED, and
121         * start it. Let's blink the led slowly (0.5Hz).
122         */
123        *delay_on = 1000; /* ms */
124        *delay_off = 1000; /* ms */
125        i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
126        status = 0;
127
128    } else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
129        /* blink the led with 1Hz */
130        i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
131        status = 0;
132
133    } else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
134        /* blink the led with 0.5Hz */
135        i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
136        status = 0;
137
138    } else {
139        pr_debug("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 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(clevo_mail_led_dmi_table);
184    } else {
185        count = 1;
186        pr_err("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            pr_err("Can't probe platform driver\n");
201            platform_device_unregister(pdev);
202        }
203    } else
204        error = PTR_ERR(pdev);
205
206    return error;
207}
208
209static void __exit clevo_mail_led_exit(void)
210{
211    platform_device_unregister(pdev);
212    platform_driver_unregister(&clevo_mail_led_driver);
213
214    clevo_mail_led_set(NULL, LED_OFF);
215}
216
217module_init(clevo_mail_led_init);
218module_exit(clevo_mail_led_exit);
219

Archive Download this file



interactive