• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/platform/x86/
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/rfkill.h>
62#include <linux/i8042.h>
63
64#define MSI_DRIVER_VERSION "0.5"
65
66#define MSI_LCD_LEVEL_MAX 9
67
68#define MSI_EC_COMMAND_WIRELESS 0x10
69#define MSI_EC_COMMAND_LCD_LEVEL 0x11
70
71#define MSI_STANDARD_EC_COMMAND_ADDRESS	0x2e
72#define MSI_STANDARD_EC_BLUETOOTH_MASK	(1 << 0)
73#define MSI_STANDARD_EC_WEBCAM_MASK	(1 << 1)
74#define MSI_STANDARD_EC_WLAN_MASK	(1 << 3)
75#define MSI_STANDARD_EC_3G_MASK		(1 << 4)
76
77/* For set SCM load flag to disable BIOS fn key */
78#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS	0x2d
79#define MSI_STANDARD_EC_SCM_LOAD_MASK		(1 << 0)
80
81static int msi_laptop_resume(struct platform_device *device);
82
83#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS	0x2f
84
85static int force;
86module_param(force, bool, 0);
87MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
88
89static int auto_brightness;
90module_param(auto_brightness, int, 0);
91MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
92
93static bool old_ec_model;
94static int wlan_s, bluetooth_s, threeg_s;
95static int threeg_exists;
96
97/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
98 * those netbook will load the SCM (windows app) to disable the original
99 * Wlan/Bluetooth control by BIOS when user press fn key, then control
100 * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
101 * cann't on/off 3G module on those 3G netbook.
102 * On Linux, msi-laptop driver will do the same thing to disable the
103 * original BIOS control, then might need use HAL or other userland
104 * application to do the software control that simulate with SCM.
105 * e.g. MSI N034 netbook
106 */
107static bool load_scm_model;
108static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
109
110/* Hardware access */
111
112static int set_lcd_level(int level)
113{
114	u8 buf[2];
115
116	if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
117		return -EINVAL;
118
119	buf[0] = 0x80;
120	buf[1] = (u8) (level*31);
121
122	return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
123			      NULL, 0, 1);
124}
125
126static int get_lcd_level(void)
127{
128	u8 wdata = 0, rdata;
129	int result;
130
131	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
132				&rdata, 1, 1);
133	if (result < 0)
134		return result;
135
136	return (int) rdata / 31;
137}
138
139static int get_auto_brightness(void)
140{
141	u8 wdata = 4, rdata;
142	int result;
143
144	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
145				&rdata, 1, 1);
146	if (result < 0)
147		return result;
148
149	return !!(rdata & 8);
150}
151
152static int set_auto_brightness(int enable)
153{
154	u8 wdata[2], rdata;
155	int result;
156
157	wdata[0] = 4;
158
159	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
160				&rdata, 1, 1);
161	if (result < 0)
162		return result;
163
164	wdata[0] = 0x84;
165	wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
166
167	return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
168			      NULL, 0, 1);
169}
170
171static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
172{
173	int status;
174	u8 wdata = 0, rdata;
175	int result;
176
177	if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
178		return -EINVAL;
179
180	/* read current device state */
181	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
182	if (result < 0)
183		return -EINVAL;
184
185	if (!!(rdata & mask) != status) {
186		/* reverse device bit */
187		if (rdata & mask)
188			wdata = rdata & ~mask;
189		else
190			wdata = rdata | mask;
191
192		result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
193		if (result < 0)
194			return -EINVAL;
195	}
196
197	return count;
198}
199
200static int get_wireless_state(int *wlan, int *bluetooth)
201{
202	u8 wdata = 0, rdata;
203	int result;
204
205	result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
206	if (result < 0)
207		return -1;
208
209	if (wlan)
210		*wlan = !!(rdata & 8);
211
212	if (bluetooth)
213		*bluetooth = !!(rdata & 128);
214
215	return 0;
216}
217
218static int get_wireless_state_ec_standard(void)
219{
220	u8 rdata;
221	int result;
222
223	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
224	if (result < 0)
225		return -1;
226
227	wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
228
229	bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
230
231	threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
232
233	return 0;
234}
235
236static int get_threeg_exists(void)
237{
238	u8 rdata;
239	int result;
240
241	result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
242	if (result < 0)
243		return -1;
244
245	threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
246
247	return 0;
248}
249
250/* Backlight device stuff */
251
252static int bl_get_brightness(struct backlight_device *b)
253{
254	return get_lcd_level();
255}
256
257
258static int bl_update_status(struct backlight_device *b)
259{
260	return set_lcd_level(b->props.brightness);
261}
262
263static const struct backlight_ops msibl_ops = {
264	.get_brightness = bl_get_brightness,
265	.update_status  = bl_update_status,
266};
267
268static struct backlight_device *msibl_device;
269
270/* Platform device */
271
272static ssize_t show_wlan(struct device *dev,
273	struct device_attribute *attr, char *buf)
274{
275
276	int ret, enabled;
277
278	if (old_ec_model) {
279		ret = get_wireless_state(&enabled, NULL);
280	} else {
281		ret = get_wireless_state_ec_standard();
282		enabled = wlan_s;
283	}
284	if (ret < 0)
285		return ret;
286
287	return sprintf(buf, "%i\n", enabled);
288}
289
290static ssize_t store_wlan(struct device *dev,
291	struct device_attribute *attr, const char *buf, size_t count)
292{
293	return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
294}
295
296static ssize_t show_bluetooth(struct device *dev,
297	struct device_attribute *attr, char *buf)
298{
299
300	int ret, enabled;
301
302	if (old_ec_model) {
303		ret = get_wireless_state(NULL, &enabled);
304	} else {
305		ret = get_wireless_state_ec_standard();
306		enabled = bluetooth_s;
307	}
308	if (ret < 0)
309		return ret;
310
311	return sprintf(buf, "%i\n", enabled);
312}
313
314static ssize_t store_bluetooth(struct device *dev,
315	struct device_attribute *attr, const char *buf, size_t count)
316{
317	return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
318}
319
320static ssize_t show_threeg(struct device *dev,
321	struct device_attribute *attr, char *buf)
322{
323
324	int ret;
325
326	/* old msi ec not support 3G */
327	if (old_ec_model)
328		return -1;
329
330	ret = get_wireless_state_ec_standard();
331	if (ret < 0)
332		return ret;
333
334	return sprintf(buf, "%i\n", threeg_s);
335}
336
337static ssize_t store_threeg(struct device *dev,
338	struct device_attribute *attr, const char *buf, size_t count)
339{
340	return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
341}
342
343static ssize_t show_lcd_level(struct device *dev,
344	struct device_attribute *attr, char *buf)
345{
346
347	int ret;
348
349	ret = get_lcd_level();
350	if (ret < 0)
351		return ret;
352
353	return sprintf(buf, "%i\n", ret);
354}
355
356static ssize_t store_lcd_level(struct device *dev,
357	struct device_attribute *attr, const char *buf, size_t count)
358{
359
360	int level, ret;
361
362	if (sscanf(buf, "%i", &level) != 1 ||
363	    (level < 0 || level >= MSI_LCD_LEVEL_MAX))
364		return -EINVAL;
365
366	ret = set_lcd_level(level);
367	if (ret < 0)
368		return ret;
369
370	return count;
371}
372
373static ssize_t show_auto_brightness(struct device *dev,
374	struct device_attribute *attr, char *buf)
375{
376
377	int ret;
378
379	ret = get_auto_brightness();
380	if (ret < 0)
381		return ret;
382
383	return sprintf(buf, "%i\n", ret);
384}
385
386static ssize_t store_auto_brightness(struct device *dev,
387	struct device_attribute *attr, const char *buf, size_t count)
388{
389
390	int enable, ret;
391
392	if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
393		return -EINVAL;
394
395	ret = set_auto_brightness(enable);
396	if (ret < 0)
397		return ret;
398
399	return count;
400}
401
402static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
403static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
404		   store_auto_brightness);
405static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
406static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
407static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
408
409static struct attribute *msipf_attributes[] = {
410	&dev_attr_lcd_level.attr,
411	&dev_attr_auto_brightness.attr,
412	&dev_attr_bluetooth.attr,
413	&dev_attr_wlan.attr,
414	NULL
415};
416
417static struct attribute_group msipf_attribute_group = {
418	.attrs = msipf_attributes
419};
420
421static struct platform_driver msipf_driver = {
422	.driver = {
423		.name = "msi-laptop-pf",
424		.owner = THIS_MODULE,
425	},
426	.resume = msi_laptop_resume,
427};
428
429static struct platform_device *msipf_device;
430
431/* Initialization */
432
433static int dmi_check_cb(const struct dmi_system_id *id)
434{
435	printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n",
436	       id->ident);
437	return 1;
438}
439
440static struct dmi_system_id __initdata msi_dmi_table[] = {
441	{
442		.ident = "MSI S270",
443		.matches = {
444			DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
445			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
446			DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
447			DMI_MATCH(DMI_CHASSIS_VENDOR,
448				  "MICRO-STAR INT'L CO.,LTD")
449		},
450		.callback = dmi_check_cb
451	},
452	{
453		.ident = "MSI S271",
454		.matches = {
455			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
456			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
457			DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
458			DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
459		},
460		.callback = dmi_check_cb
461	},
462	{
463		.ident = "MSI S420",
464		.matches = {
465			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
466			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
467			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
468			DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
469		},
470		.callback = dmi_check_cb
471	},
472	{
473		.ident = "Medion MD96100",
474		.matches = {
475			DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
476			DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
477			DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
478			DMI_MATCH(DMI_CHASSIS_VENDOR,
479				  "MICRO-STAR INT'L CO.,LTD")
480		},
481		.callback = dmi_check_cb
482	},
483	{ }
484};
485
486static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
487	{
488		.ident = "MSI N034",
489		.matches = {
490			DMI_MATCH(DMI_SYS_VENDOR,
491				"MICRO-STAR INTERNATIONAL CO., LTD"),
492			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
493			DMI_MATCH(DMI_CHASSIS_VENDOR,
494			"MICRO-STAR INTERNATIONAL CO., LTD")
495		},
496		.callback = dmi_check_cb
497	},
498	{
499		.ident = "MSI N051",
500		.matches = {
501			DMI_MATCH(DMI_SYS_VENDOR,
502				"MICRO-STAR INTERNATIONAL CO., LTD"),
503			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
504			DMI_MATCH(DMI_CHASSIS_VENDOR,
505			"MICRO-STAR INTERNATIONAL CO., LTD")
506		},
507		.callback = dmi_check_cb
508	},
509	{
510		.ident = "MSI N014",
511		.matches = {
512			DMI_MATCH(DMI_SYS_VENDOR,
513				"MICRO-STAR INTERNATIONAL CO., LTD"),
514			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
515		},
516		.callback = dmi_check_cb
517	},
518	{
519		.ident = "MSI CR620",
520		.matches = {
521			DMI_MATCH(DMI_SYS_VENDOR,
522				"Micro-Star International"),
523			DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
524		},
525		.callback = dmi_check_cb
526	},
527	{ }
528};
529
530static int rfkill_bluetooth_set(void *data, bool blocked)
531{
532	/* Do something with blocked...*/
533	/*
534	 * blocked == false is on
535	 * blocked == true is off
536	 */
537	if (blocked)
538		set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
539	else
540		set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
541
542	return 0;
543}
544
545static int rfkill_wlan_set(void *data, bool blocked)
546{
547	if (blocked)
548		set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
549	else
550		set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
551
552	return 0;
553}
554
555static int rfkill_threeg_set(void *data, bool blocked)
556{
557	if (blocked)
558		set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
559	else
560		set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
561
562	return 0;
563}
564
565static const struct rfkill_ops rfkill_bluetooth_ops = {
566	.set_block = rfkill_bluetooth_set
567};
568
569static const struct rfkill_ops rfkill_wlan_ops = {
570	.set_block = rfkill_wlan_set
571};
572
573static const struct rfkill_ops rfkill_threeg_ops = {
574	.set_block = rfkill_threeg_set
575};
576
577static void rfkill_cleanup(void)
578{
579	if (rfk_bluetooth) {
580		rfkill_unregister(rfk_bluetooth);
581		rfkill_destroy(rfk_bluetooth);
582	}
583
584	if (rfk_threeg) {
585		rfkill_unregister(rfk_threeg);
586		rfkill_destroy(rfk_threeg);
587	}
588
589	if (rfk_wlan) {
590		rfkill_unregister(rfk_wlan);
591		rfkill_destroy(rfk_wlan);
592	}
593}
594
595static void msi_update_rfkill(struct work_struct *ignored)
596{
597	get_wireless_state_ec_standard();
598
599	if (rfk_wlan)
600		rfkill_set_sw_state(rfk_wlan, !wlan_s);
601	if (rfk_bluetooth)
602		rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
603	if (rfk_threeg)
604		rfkill_set_sw_state(rfk_threeg, !threeg_s);
605}
606static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
607
608static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
609				struct serio *port)
610{
611	static bool extended;
612
613	if (str & 0x20)
614		return false;
615
616	/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/
617	if (unlikely(data == 0xe0)) {
618		extended = true;
619		return false;
620	} else if (unlikely(extended)) {
621		switch (data) {
622		case 0x54:
623		case 0x62:
624		case 0x76:
625			schedule_delayed_work(&msi_rfkill_work,
626				round_jiffies_relative(0.5 * HZ));
627			break;
628		}
629		extended = false;
630	}
631
632	return false;
633}
634
635static void msi_init_rfkill(struct work_struct *ignored)
636{
637	if (rfk_wlan) {
638		rfkill_set_sw_state(rfk_wlan, !wlan_s);
639		rfkill_wlan_set(NULL, !wlan_s);
640	}
641	if (rfk_bluetooth) {
642		rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
643		rfkill_bluetooth_set(NULL, !bluetooth_s);
644	}
645	if (rfk_threeg) {
646		rfkill_set_sw_state(rfk_threeg, !threeg_s);
647		rfkill_threeg_set(NULL, !threeg_s);
648	}
649}
650static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
651
652static int rfkill_init(struct platform_device *sdev)
653{
654	/* add rfkill */
655	int retval;
656
657	/* keep the hardware wireless state */
658	get_wireless_state_ec_standard();
659
660	rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
661				RFKILL_TYPE_BLUETOOTH,
662				&rfkill_bluetooth_ops, NULL);
663	if (!rfk_bluetooth) {
664		retval = -ENOMEM;
665		goto err_bluetooth;
666	}
667	retval = rfkill_register(rfk_bluetooth);
668	if (retval)
669		goto err_bluetooth;
670
671	rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
672				&rfkill_wlan_ops, NULL);
673	if (!rfk_wlan) {
674		retval = -ENOMEM;
675		goto err_wlan;
676	}
677	retval = rfkill_register(rfk_wlan);
678	if (retval)
679		goto err_wlan;
680
681	if (threeg_exists) {
682		rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
683				RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
684		if (!rfk_threeg) {
685			retval = -ENOMEM;
686			goto err_threeg;
687		}
688		retval = rfkill_register(rfk_threeg);
689		if (retval)
690			goto err_threeg;
691	}
692
693	/* schedule to run rfkill state initial */
694	schedule_delayed_work(&msi_rfkill_init,
695				round_jiffies_relative(1 * HZ));
696
697	return 0;
698
699err_threeg:
700	rfkill_destroy(rfk_threeg);
701	if (rfk_wlan)
702		rfkill_unregister(rfk_wlan);
703err_wlan:
704	rfkill_destroy(rfk_wlan);
705	if (rfk_bluetooth)
706		rfkill_unregister(rfk_bluetooth);
707err_bluetooth:
708	rfkill_destroy(rfk_bluetooth);
709
710	return retval;
711}
712
713static int msi_laptop_resume(struct platform_device *device)
714{
715	u8 data;
716	int result;
717
718	if (!load_scm_model)
719		return 0;
720
721	/* set load SCM to disable hardware control by fn key */
722	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
723	if (result < 0)
724		return result;
725
726	result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
727		data | MSI_STANDARD_EC_SCM_LOAD_MASK);
728	if (result < 0)
729		return result;
730
731	return 0;
732}
733
734static int load_scm_model_init(struct platform_device *sdev)
735{
736	u8 data;
737	int result;
738
739	/* allow userland write sysfs file  */
740	dev_attr_bluetooth.store = store_bluetooth;
741	dev_attr_wlan.store = store_wlan;
742	dev_attr_threeg.store = store_threeg;
743	dev_attr_bluetooth.attr.mode |= S_IWUSR;
744	dev_attr_wlan.attr.mode |= S_IWUSR;
745	dev_attr_threeg.attr.mode |= S_IWUSR;
746
747	/* disable hardware control by fn key */
748	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
749	if (result < 0)
750		return result;
751
752	result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
753		data | MSI_STANDARD_EC_SCM_LOAD_MASK);
754	if (result < 0)
755		return result;
756
757	/* initial rfkill */
758	result = rfkill_init(sdev);
759	if (result < 0)
760		goto fail_rfkill;
761
762	result = i8042_install_filter(msi_laptop_i8042_filter);
763	if (result) {
764		printk(KERN_ERR
765			"msi-laptop: Unable to install key filter\n");
766		goto fail_filter;
767	}
768
769	return 0;
770
771fail_filter:
772	rfkill_cleanup();
773
774fail_rfkill:
775
776	return result;
777
778}
779
780static int __init msi_init(void)
781{
782	int ret;
783
784	if (acpi_disabled)
785		return -ENODEV;
786
787	if (force || dmi_check_system(msi_dmi_table))
788		old_ec_model = 1;
789
790	if (!old_ec_model)
791		get_threeg_exists();
792
793	if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
794		load_scm_model = 1;
795
796	if (auto_brightness < 0 || auto_brightness > 2)
797		return -EINVAL;
798
799	/* Register backlight stuff */
800
801	if (acpi_video_backlight_support()) {
802		printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
803		       "by ACPI video driver\n");
804	} else {
805		struct backlight_properties props;
806		memset(&props, 0, sizeof(struct backlight_properties));
807		props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
808		msibl_device = backlight_device_register("msi-laptop-bl", NULL,
809							 NULL, &msibl_ops,
810							 &props);
811		if (IS_ERR(msibl_device))
812			return PTR_ERR(msibl_device);
813	}
814
815	ret = platform_driver_register(&msipf_driver);
816	if (ret)
817		goto fail_backlight;
818
819	/* Register platform stuff */
820
821	msipf_device = platform_device_alloc("msi-laptop-pf", -1);
822	if (!msipf_device) {
823		ret = -ENOMEM;
824		goto fail_platform_driver;
825	}
826
827	ret = platform_device_add(msipf_device);
828	if (ret)
829		goto fail_platform_device1;
830
831	if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
832		ret = -EINVAL;
833		goto fail_platform_device1;
834	}
835
836	ret = sysfs_create_group(&msipf_device->dev.kobj,
837				 &msipf_attribute_group);
838	if (ret)
839		goto fail_platform_device2;
840
841	if (!old_ec_model) {
842		if (threeg_exists)
843			ret = device_create_file(&msipf_device->dev,
844						&dev_attr_threeg);
845		if (ret)
846			goto fail_platform_device2;
847	}
848
849	/* Disable automatic brightness control by default because
850	 * this module was probably loaded to do brightness control in
851	 * software. */
852
853	if (auto_brightness != 2)
854		set_auto_brightness(auto_brightness);
855
856	printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
857
858	return 0;
859
860fail_platform_device2:
861
862	if (load_scm_model) {
863		i8042_remove_filter(msi_laptop_i8042_filter);
864		cancel_delayed_work_sync(&msi_rfkill_work);
865		rfkill_cleanup();
866	}
867	platform_device_del(msipf_device);
868
869fail_platform_device1:
870
871	platform_device_put(msipf_device);
872
873fail_platform_driver:
874
875	platform_driver_unregister(&msipf_driver);
876
877fail_backlight:
878
879	backlight_device_unregister(msibl_device);
880
881	return ret;
882}
883
884static void __exit msi_cleanup(void)
885{
886	if (load_scm_model) {
887		i8042_remove_filter(msi_laptop_i8042_filter);
888		cancel_delayed_work_sync(&msi_rfkill_work);
889		rfkill_cleanup();
890	}
891
892	sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
893	if (!old_ec_model && threeg_exists)
894		device_remove_file(&msipf_device->dev, &dev_attr_threeg);
895	platform_device_unregister(msipf_device);
896	platform_driver_unregister(&msipf_driver);
897	backlight_device_unregister(msibl_device);
898
899	/* Enable automatic brightness control again */
900	if (auto_brightness != 2)
901		set_auto_brightness(1);
902
903	printk(KERN_INFO "msi-laptop: driver unloaded.\n");
904}
905
906module_init(msi_init);
907module_exit(msi_cleanup);
908
909MODULE_AUTHOR("Lennart Poettering");
910MODULE_DESCRIPTION("MSI Laptop Support");
911MODULE_VERSION(MSI_DRIVER_VERSION);
912MODULE_LICENSE("GPL");
913
914MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
915MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
916MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
917MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
918MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
919MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
920MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
921MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
922