Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.23-rc3 434 lines 10 kB view raw
1/*-*-linux-c-*-*/ 2 3/* 4 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301, USA. 20 */ 21 22/* 23 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under 24 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100". 25 * 26 * Driver also supports S271, S420 models. 27 * 28 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/: 29 * 30 * lcd_level - Screen brightness: contains a single integer in the 31 * range 0..8. (rw) 32 * 33 * auto_brightness - Enable automatic brightness control: contains 34 * either 0 or 1. If set to 1 the hardware adjusts the screen 35 * brightness automatically when the power cord is 36 * plugged/unplugged. (rw) 37 * 38 * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro) 39 * 40 * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1 41 * Please note that this file is constantly 0 if no Bluetooth 42 * hardware is available. (ro) 43 * 44 * In addition to these platform device attributes the driver 45 * registers itself in the Linux backlight control subsystem and is 46 * available to userspace under /sys/class/backlight/msi-laptop-bl/. 47 * 48 * This driver might work on other laptops produced by MSI. If you 49 * want to try it you can pass force=1 as argument to the module which 50 * will force it to load even when the DMI data doesn't identify the 51 * laptop as MSI S270. YMMV. 52 */ 53 54#include <linux/module.h> 55#include <linux/kernel.h> 56#include <linux/init.h> 57#include <linux/acpi.h> 58#include <linux/dmi.h> 59#include <linux/backlight.h> 60#include <linux/platform_device.h> 61#include <linux/autoconf.h> 62 63#define MSI_DRIVER_VERSION "0.5" 64 65#define MSI_LCD_LEVEL_MAX 9 66 67#define MSI_EC_COMMAND_WIRELESS 0x10 68#define MSI_EC_COMMAND_LCD_LEVEL 0x11 69 70static int force; 71module_param(force, bool, 0); 72MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); 73 74static int auto_brightness; 75module_param(auto_brightness, int, 0); 76MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); 77 78/* Hardware access */ 79 80static int set_lcd_level(int level) 81{ 82 u8 buf[2]; 83 84 if (level < 0 || level >= MSI_LCD_LEVEL_MAX) 85 return -EINVAL; 86 87 buf[0] = 0x80; 88 buf[1] = (u8) (level*31); 89 90 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1); 91} 92 93static int get_lcd_level(void) 94{ 95 u8 wdata = 0, rdata; 96 int result; 97 98 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); 99 if (result < 0) 100 return result; 101 102 return (int) rdata / 31; 103} 104 105static int get_auto_brightness(void) 106{ 107 u8 wdata = 4, rdata; 108 int result; 109 110 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); 111 if (result < 0) 112 return result; 113 114 return !!(rdata & 8); 115} 116 117static int set_auto_brightness(int enable) 118{ 119 u8 wdata[2], rdata; 120 int result; 121 122 wdata[0] = 4; 123 124 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1); 125 if (result < 0) 126 return result; 127 128 wdata[0] = 0x84; 129 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); 130 131 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); 132} 133 134static int get_wireless_state(int *wlan, int *bluetooth) 135{ 136 u8 wdata = 0, rdata; 137 int result; 138 139 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1); 140 if (result < 0) 141 return -1; 142 143 if (wlan) 144 *wlan = !!(rdata & 8); 145 146 if (bluetooth) 147 *bluetooth = !!(rdata & 128); 148 149 return 0; 150} 151 152/* Backlight device stuff */ 153 154static int bl_get_brightness(struct backlight_device *b) 155{ 156 return get_lcd_level(); 157} 158 159 160static int bl_update_status(struct backlight_device *b) 161{ 162 return set_lcd_level(b->props.brightness); 163} 164 165static struct backlight_ops msibl_ops = { 166 .get_brightness = bl_get_brightness, 167 .update_status = bl_update_status, 168}; 169 170static struct backlight_device *msibl_device; 171 172/* Platform device */ 173 174static ssize_t show_wlan(struct device *dev, 175 struct device_attribute *attr, char *buf) 176{ 177 178 int ret, enabled; 179 180 ret = get_wireless_state(&enabled, NULL); 181 if (ret < 0) 182 return ret; 183 184 return sprintf(buf, "%i\n", enabled); 185} 186 187static ssize_t show_bluetooth(struct device *dev, 188 struct device_attribute *attr, char *buf) 189{ 190 191 int ret, enabled; 192 193 ret = get_wireless_state(NULL, &enabled); 194 if (ret < 0) 195 return ret; 196 197 return sprintf(buf, "%i\n", enabled); 198} 199 200static ssize_t show_lcd_level(struct device *dev, 201 struct device_attribute *attr, char *buf) 202{ 203 204 int ret; 205 206 ret = get_lcd_level(); 207 if (ret < 0) 208 return ret; 209 210 return sprintf(buf, "%i\n", ret); 211} 212 213static ssize_t store_lcd_level(struct device *dev, 214 struct device_attribute *attr, const char *buf, size_t count) 215{ 216 217 int level, ret; 218 219 if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX)) 220 return -EINVAL; 221 222 ret = set_lcd_level(level); 223 if (ret < 0) 224 return ret; 225 226 return count; 227} 228 229static ssize_t show_auto_brightness(struct device *dev, 230 struct device_attribute *attr, char *buf) 231{ 232 233 int ret; 234 235 ret = get_auto_brightness(); 236 if (ret < 0) 237 return ret; 238 239 return sprintf(buf, "%i\n", ret); 240} 241 242static ssize_t store_auto_brightness(struct device *dev, 243 struct device_attribute *attr, const char *buf, size_t count) 244{ 245 246 int enable, ret; 247 248 if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1))) 249 return -EINVAL; 250 251 ret = set_auto_brightness(enable); 252 if (ret < 0) 253 return ret; 254 255 return count; 256} 257 258static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 259static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); 260static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); 261static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); 262 263static struct attribute *msipf_attributes[] = { 264 &dev_attr_lcd_level.attr, 265 &dev_attr_auto_brightness.attr, 266 &dev_attr_bluetooth.attr, 267 &dev_attr_wlan.attr, 268 NULL 269}; 270 271static struct attribute_group msipf_attribute_group = { 272 .attrs = msipf_attributes 273}; 274 275static struct platform_driver msipf_driver = { 276 .driver = { 277 .name = "msi-laptop-pf", 278 .owner = THIS_MODULE, 279 } 280}; 281 282static struct platform_device *msipf_device; 283 284/* Initialization */ 285 286static int dmi_check_cb(struct dmi_system_id *id) 287{ 288 printk("msi-laptop: Identified laptop model '%s'.\n", id->ident); 289 return 0; 290} 291 292static struct dmi_system_id __initdata msi_dmi_table[] = { 293 { 294 .ident = "MSI S270", 295 .matches = { 296 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), 297 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), 298 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), 299 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") 300 }, 301 .callback = dmi_check_cb 302 }, 303 { 304 .ident = "MSI S271", 305 .matches = { 306 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 307 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"), 308 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"), 309 DMI_MATCH(DMI_BOARD_NAME, "MS-1058") 310 }, 311 .callback = dmi_check_cb 312 }, 313 { 314 .ident = "MSI S420", 315 .matches = { 316 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 317 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"), 318 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), 319 DMI_MATCH(DMI_BOARD_NAME, "MS-1412") 320 }, 321 .callback = dmi_check_cb 322 }, 323 { 324 .ident = "Medion MD96100", 325 .matches = { 326 DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), 327 DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), 328 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), 329 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") 330 }, 331 .callback = dmi_check_cb 332 }, 333 { } 334}; 335 336static int __init msi_init(void) 337{ 338 int ret; 339 340 if (acpi_disabled) 341 return -ENODEV; 342 343 if (!force && !dmi_check_system(msi_dmi_table)) 344 return -ENODEV; 345 346 if (auto_brightness < 0 || auto_brightness > 2) 347 return -EINVAL; 348 349 /* Register backlight stuff */ 350 351 msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL, 352 &msibl_ops); 353 if (IS_ERR(msibl_device)) 354 return PTR_ERR(msibl_device); 355 356 msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1, 357 358 ret = platform_driver_register(&msipf_driver); 359 if (ret) 360 goto fail_backlight; 361 362 /* Register platform stuff */ 363 364 msipf_device = platform_device_alloc("msi-laptop-pf", -1); 365 if (!msipf_device) { 366 ret = -ENOMEM; 367 goto fail_platform_driver; 368 } 369 370 ret = platform_device_add(msipf_device); 371 if (ret) 372 goto fail_platform_device1; 373 374 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); 375 if (ret) 376 goto fail_platform_device2; 377 378 /* Disable automatic brightness control by default because 379 * this module was probably loaded to do brightness control in 380 * software. */ 381 382 if (auto_brightness != 2) 383 set_auto_brightness(auto_brightness); 384 385 printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n"); 386 387 return 0; 388 389fail_platform_device2: 390 391 platform_device_del(msipf_device); 392 393fail_platform_device1: 394 395 platform_device_put(msipf_device); 396 397fail_platform_driver: 398 399 platform_driver_unregister(&msipf_driver); 400 401fail_backlight: 402 403 backlight_device_unregister(msibl_device); 404 405 return ret; 406} 407 408static void __exit msi_cleanup(void) 409{ 410 411 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); 412 platform_device_unregister(msipf_device); 413 platform_driver_unregister(&msipf_driver); 414 backlight_device_unregister(msibl_device); 415 416 /* Enable automatic brightness control again */ 417 if (auto_brightness != 2) 418 set_auto_brightness(1); 419 420 printk(KERN_INFO "msi-laptop: driver unloaded.\n"); 421} 422 423module_init(msi_init); 424module_exit(msi_cleanup); 425 426MODULE_AUTHOR("Lennart Poettering"); 427MODULE_DESCRIPTION("MSI Laptop Support"); 428MODULE_VERSION(MSI_DRIVER_VERSION); 429MODULE_LICENSE("GPL"); 430 431MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 432MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); 433MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 434MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");