0001
0002 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0003
0004 #include <linux/module.h>
0005
0006 #include <linux/platform_device.h>
0007 #include <linux/err.h>
0008 #include <linux/leds.h>
0009
0010 #include <linux/io.h>
0011 #include <linux/dmi.h>
0012
0013 #include <linux/i8042.h>
0014
0015 #define CLEVO_MAIL_LED_OFF 0x0084
0016 #define CLEVO_MAIL_LED_BLINK_1HZ 0x008A
0017 #define CLEVO_MAIL_LED_BLINK_0_5HZ 0x0083
0018
0019 MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
0020 MODULE_DESCRIPTION("Clevo mail LED driver");
0021 MODULE_LICENSE("GPL");
0022
0023 static bool nodetect;
0024 module_param_named(nodetect, nodetect, bool, 0);
0025 MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
0026
0027 static struct platform_device *pdev;
0028
0029 static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
0030 {
0031 pr_info("'%s' found\n", id->ident);
0032 return 1;
0033 }
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 static const struct dmi_system_id clevo_mail_led_dmi_table[] __initconst = {
0045 {
0046 .callback = clevo_mail_led_dmi_callback,
0047 .ident = "Clevo D410J",
0048 .matches = {
0049 DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
0050 DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
0051 DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
0052 }
0053 },
0054 {
0055 .callback = clevo_mail_led_dmi_callback,
0056 .ident = "Clevo M5x0N",
0057 .matches = {
0058 DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
0059 DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
0060 }
0061 },
0062 {
0063 .callback = clevo_mail_led_dmi_callback,
0064 .ident = "Clevo M5x0V",
0065 .matches = {
0066 DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
0067 DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
0068 DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")
0069 }
0070 },
0071 {
0072 .callback = clevo_mail_led_dmi_callback,
0073 .ident = "Clevo D400P",
0074 .matches = {
0075 DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),
0076 DMI_MATCH(DMI_BOARD_NAME, "D400P"),
0077 DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),
0078 DMI_MATCH(DMI_PRODUCT_VERSION, "0106")
0079 }
0080 },
0081 {
0082 .callback = clevo_mail_led_dmi_callback,
0083 .ident = "Clevo D410V",
0084 .matches = {
0085 DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),
0086 DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),
0087 DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),
0088 DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")
0089 }
0090 },
0091 { }
0092 };
0093 MODULE_DEVICE_TABLE(dmi, clevo_mail_led_dmi_table);
0094
0095 static void clevo_mail_led_set(struct led_classdev *led_cdev,
0096 enum led_brightness value)
0097 {
0098 i8042_lock_chip();
0099
0100 if (value == LED_OFF)
0101 i8042_command(NULL, CLEVO_MAIL_LED_OFF);
0102 else if (value <= LED_HALF)
0103 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
0104 else
0105 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
0106
0107 i8042_unlock_chip();
0108
0109 }
0110
0111 static int clevo_mail_led_blink(struct led_classdev *led_cdev,
0112 unsigned long *delay_on,
0113 unsigned long *delay_off)
0114 {
0115 int status = -EINVAL;
0116
0117 i8042_lock_chip();
0118
0119 if (*delay_on == 0 && *delay_off == 0 ) {
0120
0121
0122
0123
0124 *delay_on = 1000;
0125 *delay_off = 1000;
0126 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
0127 status = 0;
0128
0129 } else if (*delay_on == 500 && *delay_off == 500 ) {
0130
0131 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
0132 status = 0;
0133
0134 } else if (*delay_on == 1000 && *delay_off == 1000 ) {
0135
0136 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
0137 status = 0;
0138
0139 } else {
0140 pr_debug("clevo_mail_led_blink(..., %lu, %lu),"
0141 " returning -EINVAL (unsupported)\n",
0142 *delay_on, *delay_off);
0143 }
0144
0145 i8042_unlock_chip();
0146
0147 return status;
0148 }
0149
0150 static struct led_classdev clevo_mail_led = {
0151 .name = "clevo::mail",
0152 .brightness_set = clevo_mail_led_set,
0153 .blink_set = clevo_mail_led_blink,
0154 .flags = LED_CORE_SUSPENDRESUME,
0155 };
0156
0157 static int __init clevo_mail_led_probe(struct platform_device *pdev)
0158 {
0159 return led_classdev_register(&pdev->dev, &clevo_mail_led);
0160 }
0161
0162 static int clevo_mail_led_remove(struct platform_device *pdev)
0163 {
0164 led_classdev_unregister(&clevo_mail_led);
0165 return 0;
0166 }
0167
0168 static struct platform_driver clevo_mail_led_driver = {
0169 .remove = clevo_mail_led_remove,
0170 .driver = {
0171 .name = KBUILD_MODNAME,
0172 },
0173 };
0174
0175 static int __init clevo_mail_led_init(void)
0176 {
0177 int error = 0;
0178 int count = 0;
0179
0180
0181 if (!nodetect) {
0182 count = dmi_check_system(clevo_mail_led_dmi_table);
0183 } else {
0184 count = 1;
0185 pr_err("Skipping DMI detection. "
0186 "If the driver works on your hardware please "
0187 "report model and the output of dmidecode in tracker "
0188 "at http://sourceforge.net/projects/clevo-mailled/\n");
0189 }
0190
0191 if (!count)
0192 return -ENODEV;
0193
0194 pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
0195 if (!IS_ERR(pdev)) {
0196 error = platform_driver_probe(&clevo_mail_led_driver,
0197 clevo_mail_led_probe);
0198 if (error) {
0199 pr_err("Can't probe platform driver\n");
0200 platform_device_unregister(pdev);
0201 }
0202 } else
0203 error = PTR_ERR(pdev);
0204
0205 return error;
0206 }
0207
0208 static void __exit clevo_mail_led_exit(void)
0209 {
0210 platform_device_unregister(pdev);
0211 platform_driver_unregister(&clevo_mail_led_driver);
0212
0213 clevo_mail_led_set(NULL, LED_OFF);
0214 }
0215
0216 module_init(clevo_mail_led_init);
0217 module_exit(clevo_mail_led_exit);