diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 63 | ||||
-rw-r--r-- | drivers/misc/Makefile | 7 | ||||
-rw-r--r-- | drivers/misc/acer-wmi.c | 145 | ||||
-rw-r--r-- | drivers/misc/atmel_pwm.c | 5 | ||||
-rw-r--r-- | drivers/misc/compal-laptop.c | 404 | ||||
-rw-r--r-- | drivers/misc/eeepc-laptop.c | 4 | ||||
-rw-r--r-- | drivers/misc/fujitsu-laptop.c | 831 | ||||
-rw-r--r-- | drivers/misc/hdpuftrs/hdpu_cpustate.c | 9 | ||||
-rw-r--r-- | drivers/misc/hp-wmi.c | 494 | ||||
-rw-r--r-- | drivers/misc/hpilo.c | 768 | ||||
-rw-r--r-- | drivers/misc/hpilo.h | 189 | ||||
-rw-r--r-- | drivers/misc/kgdbts.c | 33 | ||||
-rw-r--r-- | drivers/misc/phantom.c | 16 | ||||
-rw-r--r-- | drivers/misc/sgi-xp/xpc_main.c | 3 | ||||
-rw-r--r-- | drivers/misc/sony-laptop.c | 3 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 963 |
16 files changed, 3479 insertions, 458 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 636af2862308..321eb9134635 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -77,11 +77,13 @@ config IBM_ASM | |||
77 | for your IBM server. | 77 | for your IBM server. |
78 | 78 | ||
79 | config PHANTOM | 79 | config PHANTOM |
80 | tristate "Sensable PHANToM" | 80 | tristate "Sensable PHANToM (PCI)" |
81 | depends on PCI | 81 | depends on PCI |
82 | help | 82 | help |
83 | Say Y here if you want to build a driver for Sensable PHANToM device. | 83 | Say Y here if you want to build a driver for Sensable PHANToM device. |
84 | 84 | ||
85 | This driver is only for PCI PHANToMs. | ||
86 | |||
85 | If you choose to build module, its name will be phantom. If unsure, | 87 | If you choose to build module, its name will be phantom. If unsure, |
86 | say N here. | 88 | say N here. |
87 | 89 | ||
@@ -179,17 +181,29 @@ config FUJITSU_LAPTOP | |||
179 | tristate "Fujitsu Laptop Extras" | 181 | tristate "Fujitsu Laptop Extras" |
180 | depends on X86 | 182 | depends on X86 |
181 | depends on ACPI | 183 | depends on ACPI |
184 | depends on INPUT | ||
182 | depends on BACKLIGHT_CLASS_DEVICE | 185 | depends on BACKLIGHT_CLASS_DEVICE |
183 | ---help--- | 186 | ---help--- |
184 | This is a driver for laptops built by Fujitsu: | 187 | This is a driver for laptops built by Fujitsu: |
185 | 188 | ||
186 | * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks | 189 | * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks |
187 | * Possibly other Fujitsu laptop models | 190 | * Possibly other Fujitsu laptop models |
191 | * Tested with S6410 and S7020 | ||
188 | 192 | ||
189 | It adds support for LCD brightness control. | 193 | It adds support for LCD brightness control and some hotkeys. |
190 | 194 | ||
191 | If you have a Fujitsu laptop, say Y or M here. | 195 | If you have a Fujitsu laptop, say Y or M here. |
192 | 196 | ||
197 | config FUJITSU_LAPTOP_DEBUG | ||
198 | bool "Verbose debug mode for Fujitsu Laptop Extras" | ||
199 | depends on FUJITSU_LAPTOP | ||
200 | default n | ||
201 | ---help--- | ||
202 | Enables extra debug output from the fujitsu extras driver, at the | ||
203 | expense of a slight increase in driver size. | ||
204 | |||
205 | If you are not sure, say N here. | ||
206 | |||
193 | config TC1100_WMI | 207 | config TC1100_WMI |
194 | tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)" | 208 | tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)" |
195 | depends on X86 && !X86_64 | 209 | depends on X86 && !X86_64 |
@@ -200,6 +214,18 @@ config TC1100_WMI | |||
200 | This is a driver for the WMI extensions (wireless and bluetooth power | 214 | This is a driver for the WMI extensions (wireless and bluetooth power |
201 | control) of the HP Compaq TC1100 tablet. | 215 | control) of the HP Compaq TC1100 tablet. |
202 | 216 | ||
217 | config HP_WMI | ||
218 | tristate "HP WMI extras" | ||
219 | depends on ACPI_WMI | ||
220 | depends on INPUT | ||
221 | depends on RFKILL | ||
222 | help | ||
223 | Say Y here if you want to support WMI-based hotkeys on HP laptops and | ||
224 | to read data from WMI such as docking or ambient light sensor state. | ||
225 | |||
226 | To compile this driver as a module, choose M here: the module will | ||
227 | be called hp-wmi. | ||
228 | |||
203 | config MSI_LAPTOP | 229 | config MSI_LAPTOP |
204 | tristate "MSI Laptop Extras" | 230 | tristate "MSI Laptop Extras" |
205 | depends on X86 | 231 | depends on X86 |
@@ -219,6 +245,23 @@ config MSI_LAPTOP | |||
219 | 245 | ||
220 | If you have an MSI S270 laptop, say Y or M here. | 246 | If you have an MSI S270 laptop, say Y or M here. |
221 | 247 | ||
248 | config COMPAL_LAPTOP | ||
249 | tristate "Compal Laptop Extras" | ||
250 | depends on X86 | ||
251 | depends on ACPI_EC | ||
252 | depends on BACKLIGHT_CLASS_DEVICE | ||
253 | ---help--- | ||
254 | This is a driver for laptops built by Compal: | ||
255 | |||
256 | Compal FL90/IFL90 | ||
257 | Compal FL91/IFL91 | ||
258 | Compal FL92/JFL92 | ||
259 | Compal FT00/IFT00 | ||
260 | |||
261 | It adds support for Bluetooth, WLAN and LCD brightness control. | ||
262 | |||
263 | If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here. | ||
264 | |||
222 | config SONY_LAPTOP | 265 | config SONY_LAPTOP |
223 | tristate "Sony Laptop Extras" | 266 | tristate "Sony Laptop Extras" |
224 | depends on X86 && ACPI | 267 | depends on X86 && ACPI |
@@ -250,6 +293,8 @@ config THINKPAD_ACPI | |||
250 | select INPUT | 293 | select INPUT |
251 | select NEW_LEDS | 294 | select NEW_LEDS |
252 | select LEDS_CLASS | 295 | select LEDS_CLASS |
296 | select NET | ||
297 | select RFKILL | ||
253 | ---help--- | 298 | ---help--- |
254 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds | 299 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds |
255 | support for Fn-Fx key combinations, Bluetooth control, video | 300 | support for Fn-Fx key combinations, Bluetooth control, video |
@@ -391,4 +436,18 @@ config SGI_XP | |||
391 | this feature will allow for direct communication between SSIs | 436 | this feature will allow for direct communication between SSIs |
392 | based on a network adapter and DMA messaging. | 437 | based on a network adapter and DMA messaging. |
393 | 438 | ||
439 | config HP_ILO | ||
440 | tristate "Channel interface driver for HP iLO/iLO2 processor" | ||
441 | depends on PCI | ||
442 | default n | ||
443 | help | ||
444 | The channel interface driver allows applications to communicate | ||
445 | with iLO/iLO2 management processors present on HP ProLiant | ||
446 | servers. Upon loading, the driver creates /dev/hpilo/dXccbN files, | ||
447 | which can be used to gather data from the management processor, | ||
448 | via read and write system calls. | ||
449 | |||
450 | To compile this driver as a module, choose M here: the | ||
451 | module will be called hpilo. | ||
452 | |||
394 | endif # MISC_DEVICES | 453 | endif # MISC_DEVICES |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 1952875a272e..f5e273420c09 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -5,13 +5,15 @@ obj- := misc.o # Dummy rule to force built-in.o to be made | |||
5 | 5 | ||
6 | obj-$(CONFIG_IBM_ASM) += ibmasm/ | 6 | obj-$(CONFIG_IBM_ASM) += ibmasm/ |
7 | obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ | 7 | obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ |
8 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o | ||
9 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o | ||
10 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o | 8 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o |
11 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o | 9 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o |
10 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o | ||
11 | obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o | ||
12 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o | ||
12 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o | 13 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o |
13 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o | 14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
14 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
16 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | ||
15 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 17 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
16 | obj-$(CONFIG_LKDTM) += lkdtm.o | 18 | obj-$(CONFIG_LKDTM) += lkdtm.o |
17 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o | 19 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o |
@@ -26,3 +28,4 @@ obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o | |||
26 | obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o | 28 | obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o |
27 | obj-$(CONFIG_KGDB_TESTS) += kgdbts.o | 29 | obj-$(CONFIG_KGDB_TESTS) += kgdbts.o |
28 | obj-$(CONFIG_SGI_XP) += sgi-xp/ | 30 | obj-$(CONFIG_SGI_XP) += sgi-xp/ |
31 | obj-$(CONFIG_HP_ILO) += hpilo.o | ||
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index dd13a3749927..e7a3fe508dff 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c | |||
@@ -22,18 +22,18 @@ | |||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #define ACER_WMI_VERSION "0.1" | ||
26 | |||
27 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
28 | #include <linux/module.h> | 26 | #include <linux/module.h> |
29 | #include <linux/init.h> | 27 | #include <linux/init.h> |
30 | #include <linux/types.h> | 28 | #include <linux/types.h> |
31 | #include <linux/dmi.h> | 29 | #include <linux/dmi.h> |
30 | #include <linux/fb.h> | ||
32 | #include <linux/backlight.h> | 31 | #include <linux/backlight.h> |
33 | #include <linux/leds.h> | 32 | #include <linux/leds.h> |
34 | #include <linux/platform_device.h> | 33 | #include <linux/platform_device.h> |
35 | #include <linux/acpi.h> | 34 | #include <linux/acpi.h> |
36 | #include <linux/i8042.h> | 35 | #include <linux/i8042.h> |
36 | #include <linux/debugfs.h> | ||
37 | 37 | ||
38 | #include <acpi/acpi_drivers.h> | 38 | #include <acpi/acpi_drivers.h> |
39 | 39 | ||
@@ -87,6 +87,7 @@ struct acer_quirks { | |||
87 | * Acer ACPI method GUIDs | 87 | * Acer ACPI method GUIDs |
88 | */ | 88 | */ |
89 | #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" | 89 | #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" |
90 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" | ||
90 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" | 91 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" |
91 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" | 92 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" |
92 | 93 | ||
@@ -150,6 +151,12 @@ struct acer_data { | |||
150 | int brightness; | 151 | int brightness; |
151 | }; | 152 | }; |
152 | 153 | ||
154 | struct acer_debug { | ||
155 | struct dentry *root; | ||
156 | struct dentry *devices; | ||
157 | u32 wmid_devices; | ||
158 | }; | ||
159 | |||
153 | /* Each low-level interface must define at least some of the following */ | 160 | /* Each low-level interface must define at least some of the following */ |
154 | struct wmi_interface { | 161 | struct wmi_interface { |
155 | /* The WMI device type */ | 162 | /* The WMI device type */ |
@@ -160,6 +167,9 @@ struct wmi_interface { | |||
160 | 167 | ||
161 | /* Private data for the current interface */ | 168 | /* Private data for the current interface */ |
162 | struct acer_data data; | 169 | struct acer_data data; |
170 | |||
171 | /* debugfs entries associated with this interface */ | ||
172 | struct acer_debug debug; | ||
163 | }; | 173 | }; |
164 | 174 | ||
165 | /* The static interface pointer, points to the currently detected interface */ | 175 | /* The static interface pointer, points to the currently detected interface */ |
@@ -174,7 +184,7 @@ static struct wmi_interface *interface; | |||
174 | struct quirk_entry { | 184 | struct quirk_entry { |
175 | u8 wireless; | 185 | u8 wireless; |
176 | u8 mailled; | 186 | u8 mailled; |
177 | u8 brightness; | 187 | s8 brightness; |
178 | u8 bluetooth; | 188 | u8 bluetooth; |
179 | }; | 189 | }; |
180 | 190 | ||
@@ -198,6 +208,10 @@ static int dmi_matched(const struct dmi_system_id *dmi) | |||
198 | static struct quirk_entry quirk_unknown = { | 208 | static struct quirk_entry quirk_unknown = { |
199 | }; | 209 | }; |
200 | 210 | ||
211 | static struct quirk_entry quirk_acer_aspire_1520 = { | ||
212 | .brightness = -1, | ||
213 | }; | ||
214 | |||
201 | static struct quirk_entry quirk_acer_travelmate_2490 = { | 215 | static struct quirk_entry quirk_acer_travelmate_2490 = { |
202 | .mailled = 1, | 216 | .mailled = 1, |
203 | }; | 217 | }; |
@@ -207,9 +221,31 @@ static struct quirk_entry quirk_medion_md_98300 = { | |||
207 | .wireless = 1, | 221 | .wireless = 1, |
208 | }; | 222 | }; |
209 | 223 | ||
224 | static struct quirk_entry quirk_fujitsu_amilo_li_1718 = { | ||
225 | .wireless = 2, | ||
226 | }; | ||
227 | |||
210 | static struct dmi_system_id acer_quirks[] = { | 228 | static struct dmi_system_id acer_quirks[] = { |
211 | { | 229 | { |
212 | .callback = dmi_matched, | 230 | .callback = dmi_matched, |
231 | .ident = "Acer Aspire 1360", | ||
232 | .matches = { | ||
233 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"), | ||
234 | DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), | ||
235 | }, | ||
236 | .driver_data = &quirk_acer_aspire_1520, | ||
237 | }, | ||
238 | { | ||
239 | .callback = dmi_matched, | ||
240 | .ident = "Acer Aspire 1520", | ||
241 | .matches = { | ||
242 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"), | ||
243 | DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"), | ||
244 | }, | ||
245 | .driver_data = &quirk_acer_aspire_1520, | ||
246 | }, | ||
247 | { | ||
248 | .callback = dmi_matched, | ||
213 | .ident = "Acer Aspire 3100", | 249 | .ident = "Acer Aspire 3100", |
214 | .matches = { | 250 | .matches = { |
215 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"), | 251 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"), |
@@ -300,6 +336,15 @@ static struct dmi_system_id acer_quirks[] = { | |||
300 | }, | 336 | }, |
301 | { | 337 | { |
302 | .callback = dmi_matched, | 338 | .callback = dmi_matched, |
339 | .ident = "Fujitsu Siemens Amilo Li 1718", | ||
340 | .matches = { | ||
341 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | ||
342 | DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"), | ||
343 | }, | ||
344 | .driver_data = &quirk_fujitsu_amilo_li_1718, | ||
345 | }, | ||
346 | { | ||
347 | .callback = dmi_matched, | ||
303 | .ident = "Medion MD 98300", | 348 | .ident = "Medion MD 98300", |
304 | .matches = { | 349 | .matches = { |
305 | DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), | 350 | DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), |
@@ -393,6 +438,12 @@ struct wmi_interface *iface) | |||
393 | return AE_ERROR; | 438 | return AE_ERROR; |
394 | *value = result & 0x1; | 439 | *value = result & 0x1; |
395 | return AE_OK; | 440 | return AE_OK; |
441 | case 2: | ||
442 | err = ec_read(0x71, &result); | ||
443 | if (err) | ||
444 | return AE_ERROR; | ||
445 | *value = result & 0x1; | ||
446 | return AE_OK; | ||
396 | default: | 447 | default: |
397 | err = ec_read(0xA, &result); | 448 | err = ec_read(0xA, &result); |
398 | if (err) | 449 | if (err) |
@@ -506,6 +557,15 @@ static acpi_status AMW0_set_capabilities(void) | |||
506 | struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; | 557 | struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; |
507 | union acpi_object *obj; | 558 | union acpi_object *obj; |
508 | 559 | ||
560 | /* | ||
561 | * On laptops with this strange GUID (non Acer), normal probing doesn't | ||
562 | * work. | ||
563 | */ | ||
564 | if (wmi_has_guid(AMW0_GUID2)) { | ||
565 | interface->capability |= ACER_CAP_WIRELESS; | ||
566 | return AE_OK; | ||
567 | } | ||
568 | |||
509 | args.eax = ACER_AMW0_WRITE; | 569 | args.eax = ACER_AMW0_WRITE; |
510 | args.ecx = args.edx = 0; | 570 | args.ecx = args.edx = 0; |
511 | 571 | ||
@@ -552,7 +612,8 @@ static acpi_status AMW0_set_capabilities(void) | |||
552 | * appear to use the same EC register for brightness, even if they | 612 | * appear to use the same EC register for brightness, even if they |
553 | * differ for wireless, etc | 613 | * differ for wireless, etc |
554 | */ | 614 | */ |
555 | interface->capability |= ACER_CAP_BRIGHTNESS; | 615 | if (quirks->brightness >= 0) |
616 | interface->capability |= ACER_CAP_BRIGHTNESS; | ||
556 | 617 | ||
557 | return AE_OK; | 618 | return AE_OK; |
558 | } | 619 | } |
@@ -807,7 +868,15 @@ static int read_brightness(struct backlight_device *bd) | |||
807 | 868 | ||
808 | static int update_bl_status(struct backlight_device *bd) | 869 | static int update_bl_status(struct backlight_device *bd) |
809 | { | 870 | { |
810 | set_u32(bd->props.brightness, ACER_CAP_BRIGHTNESS); | 871 | int intensity = bd->props.brightness; |
872 | |||
873 | if (bd->props.power != FB_BLANK_UNBLANK) | ||
874 | intensity = 0; | ||
875 | if (bd->props.fb_blank != FB_BLANK_UNBLANK) | ||
876 | intensity = 0; | ||
877 | |||
878 | set_u32(intensity, ACER_CAP_BRIGHTNESS); | ||
879 | |||
811 | return 0; | 880 | return 0; |
812 | } | 881 | } |
813 | 882 | ||
@@ -829,8 +898,9 @@ static int __devinit acer_backlight_init(struct device *dev) | |||
829 | 898 | ||
830 | acer_backlight_device = bd; | 899 | acer_backlight_device = bd; |
831 | 900 | ||
901 | bd->props.power = FB_BLANK_UNBLANK; | ||
902 | bd->props.brightness = max_brightness; | ||
832 | bd->props.max_brightness = max_brightness; | 903 | bd->props.max_brightness = max_brightness; |
833 | bd->props.brightness = read_brightness(NULL); | ||
834 | backlight_update_status(bd); | 904 | backlight_update_status(bd); |
835 | return 0; | 905 | return 0; |
836 | } | 906 | } |
@@ -894,6 +964,28 @@ static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR, | |||
894 | show_interface, NULL); | 964 | show_interface, NULL); |
895 | 965 | ||
896 | /* | 966 | /* |
967 | * debugfs functions | ||
968 | */ | ||
969 | static u32 get_wmid_devices(void) | ||
970 | { | ||
971 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
972 | union acpi_object *obj; | ||
973 | acpi_status status; | ||
974 | |||
975 | status = wmi_query_block(WMID_GUID2, 1, &out); | ||
976 | if (ACPI_FAILURE(status)) | ||
977 | return 0; | ||
978 | |||
979 | obj = (union acpi_object *) out.pointer; | ||
980 | if (obj && obj->type == ACPI_TYPE_BUFFER && | ||
981 | obj->buffer.length == sizeof(u32)) { | ||
982 | return *((u32 *) obj->buffer.pointer); | ||
983 | } else { | ||
984 | return 0; | ||
985 | } | ||
986 | } | ||
987 | |||
988 | /* | ||
897 | * Platform device | 989 | * Platform device |
898 | */ | 990 | */ |
899 | static int __devinit acer_platform_probe(struct platform_device *device) | 991 | static int __devinit acer_platform_probe(struct platform_device *device) |
@@ -1052,12 +1144,40 @@ error_sysfs: | |||
1052 | return retval; | 1144 | return retval; |
1053 | } | 1145 | } |
1054 | 1146 | ||
1147 | static void remove_debugfs(void) | ||
1148 | { | ||
1149 | debugfs_remove(interface->debug.devices); | ||
1150 | debugfs_remove(interface->debug.root); | ||
1151 | } | ||
1152 | |||
1153 | static int create_debugfs(void) | ||
1154 | { | ||
1155 | interface->debug.root = debugfs_create_dir("acer-wmi", NULL); | ||
1156 | if (!interface->debug.root) { | ||
1157 | printk(ACER_ERR "Failed to create debugfs directory"); | ||
1158 | return -ENOMEM; | ||
1159 | } | ||
1160 | |||
1161 | interface->debug.devices = debugfs_create_u32("devices", S_IRUGO, | ||
1162 | interface->debug.root, | ||
1163 | &interface->debug.wmid_devices); | ||
1164 | if (!interface->debug.devices) | ||
1165 | goto error_debugfs; | ||
1166 | |||
1167 | return 0; | ||
1168 | |||
1169 | error_debugfs: | ||
1170 | remove_debugfs(); | ||
1171 | return -ENOMEM; | ||
1172 | } | ||
1173 | |||
1055 | static int __init acer_wmi_init(void) | 1174 | static int __init acer_wmi_init(void) |
1056 | { | 1175 | { |
1057 | int err; | 1176 | int err; |
1058 | 1177 | ||
1059 | printk(ACER_INFO "Acer Laptop ACPI-WMI Extras version %s\n", | 1178 | printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n"); |
1060 | ACER_WMI_VERSION); | 1179 | |
1180 | find_quirks(); | ||
1061 | 1181 | ||
1062 | /* | 1182 | /* |
1063 | * Detect which ACPI-WMI interface we're using. | 1183 | * Detect which ACPI-WMI interface we're using. |
@@ -1092,8 +1212,6 @@ static int __init acer_wmi_init(void) | |||
1092 | if (wmi_has_guid(AMW0_GUID1)) | 1212 | if (wmi_has_guid(AMW0_GUID1)) |
1093 | AMW0_find_mailled(); | 1213 | AMW0_find_mailled(); |
1094 | 1214 | ||
1095 | find_quirks(); | ||
1096 | |||
1097 | if (!interface) { | 1215 | if (!interface) { |
1098 | printk(ACER_ERR "No or unsupported WMI interface, unable to " | 1216 | printk(ACER_ERR "No or unsupported WMI interface, unable to " |
1099 | "load\n"); | 1217 | "load\n"); |
@@ -1111,6 +1229,13 @@ static int __init acer_wmi_init(void) | |||
1111 | if (err) | 1229 | if (err) |
1112 | return err; | 1230 | return err; |
1113 | 1231 | ||
1232 | if (wmi_has_guid(WMID_GUID2)) { | ||
1233 | interface->debug.wmid_devices = get_wmid_devices(); | ||
1234 | err = create_debugfs(); | ||
1235 | if (err) | ||
1236 | return err; | ||
1237 | } | ||
1238 | |||
1114 | /* Override any initial settings with values from the commandline */ | 1239 | /* Override any initial settings with values from the commandline */ |
1115 | acer_commandline_init(); | 1240 | acer_commandline_init(); |
1116 | 1241 | ||
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 0d5ce03cdff2..6aa5294dfec4 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c | |||
@@ -211,8 +211,7 @@ int pwm_clk_alloc(unsigned prescale, unsigned div) | |||
211 | if ((mr & 0xffff) == 0) { | 211 | if ((mr & 0xffff) == 0) { |
212 | mr |= val; | 212 | mr |= val; |
213 | ret = PWM_CPR_CLKA; | 213 | ret = PWM_CPR_CLKA; |
214 | } | 214 | } else if ((mr & (0xffff << 16)) == 0) { |
215 | if ((mr & (0xffff << 16)) == 0) { | ||
216 | mr |= val << 16; | 215 | mr |= val << 16; |
217 | ret = PWM_CPR_CLKB; | 216 | ret = PWM_CPR_CLKB; |
218 | } | 217 | } |
@@ -332,7 +331,7 @@ static int __init pwm_probe(struct platform_device *pdev) | |||
332 | p->base = ioremap(r->start, r->end - r->start + 1); | 331 | p->base = ioremap(r->start, r->end - r->start + 1); |
333 | if (!p->base) | 332 | if (!p->base) |
334 | goto fail; | 333 | goto fail; |
335 | p->clk = clk_get(&pdev->dev, "mck"); | 334 | p->clk = clk_get(&pdev->dev, "pwm_clk"); |
336 | if (IS_ERR(p->clk)) { | 335 | if (IS_ERR(p->clk)) { |
337 | status = PTR_ERR(p->clk); | 336 | status = PTR_ERR(p->clk); |
338 | p->clk = NULL; | 337 | p->clk = NULL; |
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c new file mode 100644 index 000000000000..344b790a6253 --- /dev/null +++ b/drivers/misc/compal-laptop.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /*-*-linux-c-*-*/ | ||
2 | |||
3 | /* | ||
4 | Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com> | ||
5 | |||
6 | based on MSI driver | ||
7 | |||
8 | Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, but | ||
16 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; if not, write to the Free Software | ||
22 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
23 | 02110-1301, USA. | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * comapl-laptop.c - Compal laptop support. | ||
28 | * | ||
29 | * This driver exports a few files in /sys/devices/platform/compal-laptop/: | ||
30 | * | ||
31 | * wlan - wlan subsystem state: contains 0 or 1 (rw) | ||
32 | * | ||
33 | * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw) | ||
34 | * | ||
35 | * raw - raw value taken from embedded controller register (ro) | ||
36 | * | ||
37 | * In addition to these platform device attributes the driver | ||
38 | * registers itself in the Linux backlight control subsystem and is | ||
39 | * available to userspace under /sys/class/backlight/compal-laptop/. | ||
40 | * | ||
41 | * This driver might work on other laptops produced by Compal. If you | ||
42 | * want to try it you can pass force=1 as argument to the module which | ||
43 | * will force it to load even when the DMI data doesn't identify the | ||
44 | * laptop as FL9x. | ||
45 | */ | ||
46 | |||
47 | #include <linux/module.h> | ||
48 | #include <linux/kernel.h> | ||
49 | #include <linux/init.h> | ||
50 | #include <linux/acpi.h> | ||
51 | #include <linux/dmi.h> | ||
52 | #include <linux/backlight.h> | ||
53 | #include <linux/platform_device.h> | ||
54 | #include <linux/autoconf.h> | ||
55 | |||
56 | #define COMPAL_DRIVER_VERSION "0.2.6" | ||
57 | |||
58 | #define COMPAL_LCD_LEVEL_MAX 8 | ||
59 | |||
60 | #define COMPAL_EC_COMMAND_WIRELESS 0xBB | ||
61 | #define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9 | ||
62 | |||
63 | #define KILLSWITCH_MASK 0x10 | ||
64 | #define WLAN_MASK 0x01 | ||
65 | #define BT_MASK 0x02 | ||
66 | |||
67 | static int force; | ||
68 | module_param(force, bool, 0); | ||
69 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | ||
70 | |||
71 | /* Hardware access */ | ||
72 | |||
73 | static int set_lcd_level(int level) | ||
74 | { | ||
75 | if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX) | ||
76 | return -EINVAL; | ||
77 | |||
78 | ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int get_lcd_level(void) | ||
84 | { | ||
85 | u8 result; | ||
86 | |||
87 | ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result); | ||
88 | |||
89 | return (int) result; | ||
90 | } | ||
91 | |||
92 | static int set_wlan_state(int state) | ||
93 | { | ||
94 | u8 result, value; | ||
95 | |||
96 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
97 | |||
98 | if ((result & KILLSWITCH_MASK) == 0) | ||
99 | return -EINVAL; | ||
100 | else { | ||
101 | if (state) | ||
102 | value = (u8) (result | WLAN_MASK); | ||
103 | else | ||
104 | value = (u8) (result & ~WLAN_MASK); | ||
105 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
106 | } | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int set_bluetooth_state(int state) | ||
112 | { | ||
113 | u8 result, value; | ||
114 | |||
115 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
116 | |||
117 | if ((result & KILLSWITCH_MASK) == 0) | ||
118 | return -EINVAL; | ||
119 | else { | ||
120 | if (state) | ||
121 | value = (u8) (result | BT_MASK); | ||
122 | else | ||
123 | value = (u8) (result & ~BT_MASK); | ||
124 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int get_wireless_state(int *wlan, int *bluetooth) | ||
131 | { | ||
132 | u8 result; | ||
133 | |||
134 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
135 | |||
136 | if (wlan) { | ||
137 | if ((result & KILLSWITCH_MASK) == 0) | ||
138 | *wlan = 0; | ||
139 | else | ||
140 | *wlan = result & WLAN_MASK; | ||
141 | } | ||
142 | |||
143 | if (bluetooth) { | ||
144 | if ((result & KILLSWITCH_MASK) == 0) | ||
145 | *bluetooth = 0; | ||
146 | else | ||
147 | *bluetooth = (result & BT_MASK) >> 1; | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | /* Backlight device stuff */ | ||
154 | |||
155 | static int bl_get_brightness(struct backlight_device *b) | ||
156 | { | ||
157 | return get_lcd_level(); | ||
158 | } | ||
159 | |||
160 | |||
161 | static int bl_update_status(struct backlight_device *b) | ||
162 | { | ||
163 | return set_lcd_level(b->props.brightness); | ||
164 | } | ||
165 | |||
166 | static struct backlight_ops compalbl_ops = { | ||
167 | .get_brightness = bl_get_brightness, | ||
168 | .update_status = bl_update_status, | ||
169 | }; | ||
170 | |||
171 | static struct backlight_device *compalbl_device; | ||
172 | |||
173 | /* Platform device */ | ||
174 | |||
175 | static ssize_t show_wlan(struct device *dev, | ||
176 | struct device_attribute *attr, char *buf) | ||
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 | |||
187 | static ssize_t show_raw(struct device *dev, | ||
188 | struct device_attribute *attr, char *buf) | ||
189 | { | ||
190 | u8 result; | ||
191 | |||
192 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
193 | |||
194 | return sprintf(buf, "%i\n", result); | ||
195 | } | ||
196 | |||
197 | static ssize_t show_bluetooth(struct device *dev, | ||
198 | struct device_attribute *attr, char *buf) | ||
199 | { | ||
200 | int ret, enabled; | ||
201 | |||
202 | ret = get_wireless_state(NULL, &enabled); | ||
203 | if (ret < 0) | ||
204 | return ret; | ||
205 | |||
206 | return sprintf(buf, "%i\n", enabled); | ||
207 | } | ||
208 | |||
209 | static ssize_t store_wlan_state(struct device *dev, | ||
210 | struct device_attribute *attr, const char *buf, size_t count) | ||
211 | { | ||
212 | int state, ret; | ||
213 | |||
214 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
215 | return -EINVAL; | ||
216 | |||
217 | ret = set_wlan_state(state); | ||
218 | if (ret < 0) | ||
219 | return ret; | ||
220 | |||
221 | return count; | ||
222 | } | ||
223 | |||
224 | static ssize_t store_bluetooth_state(struct device *dev, | ||
225 | struct device_attribute *attr, const char *buf, size_t count) | ||
226 | { | ||
227 | int state, ret; | ||
228 | |||
229 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
230 | return -EINVAL; | ||
231 | |||
232 | ret = set_bluetooth_state(state); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | |||
236 | return count; | ||
237 | } | ||
238 | |||
239 | static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); | ||
240 | static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); | ||
241 | static DEVICE_ATTR(raw, 0444, show_raw, NULL); | ||
242 | |||
243 | static struct attribute *compal_attributes[] = { | ||
244 | &dev_attr_bluetooth.attr, | ||
245 | &dev_attr_wlan.attr, | ||
246 | &dev_attr_raw.attr, | ||
247 | NULL | ||
248 | }; | ||
249 | |||
250 | static struct attribute_group compal_attribute_group = { | ||
251 | .attrs = compal_attributes | ||
252 | }; | ||
253 | |||
254 | static struct platform_driver compal_driver = { | ||
255 | .driver = { | ||
256 | .name = "compal-laptop", | ||
257 | .owner = THIS_MODULE, | ||
258 | } | ||
259 | }; | ||
260 | |||
261 | static struct platform_device *compal_device; | ||
262 | |||
263 | /* Initialization */ | ||
264 | |||
265 | static int dmi_check_cb(const struct dmi_system_id *id) | ||
266 | { | ||
267 | printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n", | ||
268 | id->ident); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static struct dmi_system_id __initdata compal_dmi_table[] = { | ||
274 | { | ||
275 | .ident = "FL90/IFL90", | ||
276 | .matches = { | ||
277 | DMI_MATCH(DMI_BOARD_NAME, "IFL90"), | ||
278 | DMI_MATCH(DMI_BOARD_VERSION, "IFT00"), | ||
279 | }, | ||
280 | .callback = dmi_check_cb | ||
281 | }, | ||
282 | { | ||
283 | .ident = "FL90/IFL90", | ||
284 | .matches = { | ||
285 | DMI_MATCH(DMI_BOARD_NAME, "IFL90"), | ||
286 | DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"), | ||
287 | }, | ||
288 | .callback = dmi_check_cb | ||
289 | }, | ||
290 | { | ||
291 | .ident = "FL91/IFL91", | ||
292 | .matches = { | ||
293 | DMI_MATCH(DMI_BOARD_NAME, "IFL91"), | ||
294 | DMI_MATCH(DMI_BOARD_VERSION, "IFT00"), | ||
295 | }, | ||
296 | .callback = dmi_check_cb | ||
297 | }, | ||
298 | { | ||
299 | .ident = "FL92/JFL92", | ||
300 | .matches = { | ||
301 | DMI_MATCH(DMI_BOARD_NAME, "JFL92"), | ||
302 | DMI_MATCH(DMI_BOARD_VERSION, "IFT00"), | ||
303 | }, | ||
304 | .callback = dmi_check_cb | ||
305 | }, | ||
306 | { | ||
307 | .ident = "FT00/IFT00", | ||
308 | .matches = { | ||
309 | DMI_MATCH(DMI_BOARD_NAME, "IFT00"), | ||
310 | DMI_MATCH(DMI_BOARD_VERSION, "IFT00"), | ||
311 | }, | ||
312 | .callback = dmi_check_cb | ||
313 | }, | ||
314 | { } | ||
315 | }; | ||
316 | |||
317 | static int __init compal_init(void) | ||
318 | { | ||
319 | int ret; | ||
320 | |||
321 | if (acpi_disabled) | ||
322 | return -ENODEV; | ||
323 | |||
324 | if (!force && !dmi_check_system(compal_dmi_table)) | ||
325 | return -ENODEV; | ||
326 | |||
327 | /* Register backlight stuff */ | ||
328 | |||
329 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, | ||
330 | &compalbl_ops); | ||
331 | if (IS_ERR(compalbl_device)) | ||
332 | return PTR_ERR(compalbl_device); | ||
333 | |||
334 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; | ||
335 | |||
336 | ret = platform_driver_register(&compal_driver); | ||
337 | if (ret) | ||
338 | goto fail_backlight; | ||
339 | |||
340 | /* Register platform stuff */ | ||
341 | |||
342 | compal_device = platform_device_alloc("compal-laptop", -1); | ||
343 | if (!compal_device) { | ||
344 | ret = -ENOMEM; | ||
345 | goto fail_platform_driver; | ||
346 | } | ||
347 | |||
348 | ret = platform_device_add(compal_device); | ||
349 | if (ret) | ||
350 | goto fail_platform_device1; | ||
351 | |||
352 | ret = sysfs_create_group(&compal_device->dev.kobj, | ||
353 | &compal_attribute_group); | ||
354 | if (ret) | ||
355 | goto fail_platform_device2; | ||
356 | |||
357 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION | ||
358 | " successfully loaded.\n"); | ||
359 | |||
360 | return 0; | ||
361 | |||
362 | fail_platform_device2: | ||
363 | |||
364 | platform_device_del(compal_device); | ||
365 | |||
366 | fail_platform_device1: | ||
367 | |||
368 | platform_device_put(compal_device); | ||
369 | |||
370 | fail_platform_driver: | ||
371 | |||
372 | platform_driver_unregister(&compal_driver); | ||
373 | |||
374 | fail_backlight: | ||
375 | |||
376 | backlight_device_unregister(compalbl_device); | ||
377 | |||
378 | return ret; | ||
379 | } | ||
380 | |||
381 | static void __exit compal_cleanup(void) | ||
382 | { | ||
383 | |||
384 | sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); | ||
385 | platform_device_unregister(compal_device); | ||
386 | platform_driver_unregister(&compal_driver); | ||
387 | backlight_device_unregister(compalbl_device); | ||
388 | |||
389 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); | ||
390 | } | ||
391 | |||
392 | module_init(compal_init); | ||
393 | module_exit(compal_cleanup); | ||
394 | |||
395 | MODULE_AUTHOR("Cezary Jackiewicz"); | ||
396 | MODULE_DESCRIPTION("Compal Laptop Support"); | ||
397 | MODULE_VERSION(COMPAL_DRIVER_VERSION); | ||
398 | MODULE_LICENSE("GPL"); | ||
399 | |||
400 | MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*"); | ||
401 | MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | ||
402 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | ||
403 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | ||
404 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | ||
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 6d727609097f..9e8d79e7e9f4 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c | |||
@@ -87,7 +87,7 @@ enum { | |||
87 | CM_ASL_LID | 87 | CM_ASL_LID |
88 | }; | 88 | }; |
89 | 89 | ||
90 | const char *cm_getv[] = { | 90 | static const char *cm_getv[] = { |
91 | "WLDG", NULL, NULL, NULL, | 91 | "WLDG", NULL, NULL, NULL, |
92 | "CAMG", NULL, NULL, NULL, | 92 | "CAMG", NULL, NULL, NULL, |
93 | NULL, "PBLG", NULL, NULL, | 93 | NULL, "PBLG", NULL, NULL, |
@@ -96,7 +96,7 @@ const char *cm_getv[] = { | |||
96 | "CRDG", "LIDG" | 96 | "CRDG", "LIDG" |
97 | }; | 97 | }; |
98 | 98 | ||
99 | const char *cm_setv[] = { | 99 | static const char *cm_setv[] = { |
100 | "WLDS", NULL, NULL, NULL, | 100 | "WLDS", NULL, NULL, NULL, |
101 | "CAMS", NULL, NULL, NULL, | 101 | "CAMS", NULL, NULL, NULL, |
102 | "SDSP", "PBLS", "HDPS", NULL, | 102 | "SDSP", "PBLS", "HDPS", NULL, |
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index e2e7c05a147b..7a1ef6c262de 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c | |||
@@ -1,12 +1,14 @@ | |||
1 | /*-*-linux-c-*-*/ | 1 | /*-*-linux-c-*-*/ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> | 4 | Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> |
5 | Copyright (C) 2008 Peter Gruber <nokos@gmx.net> | ||
5 | Based on earlier work: | 6 | Based on earlier work: |
6 | Copyright (C) 2003 Shane Spencer <shane@bogomip.com> | 7 | Copyright (C) 2003 Shane Spencer <shane@bogomip.com> |
7 | Adrian Yee <brewt-fujitsu@brewt.org> | 8 | Adrian Yee <brewt-fujitsu@brewt.org> |
8 | 9 | ||
9 | Templated from msi-laptop.c which is copyright by its respective authors. | 10 | Templated from msi-laptop.c and thinkpad_acpi.c which is copyright |
11 | by its respective authors. | ||
10 | 12 | ||
11 | This program is free software; you can redistribute it and/or modify | 13 | This program is free software; you can redistribute it and/or modify |
12 | it under the terms of the GNU General Public License as published by | 14 | it under the terms of the GNU General Public License as published by |
@@ -39,8 +41,17 @@ | |||
39 | * registers itself in the Linux backlight control subsystem and is | 41 | * registers itself in the Linux backlight control subsystem and is |
40 | * available to userspace under /sys/class/backlight/fujitsu-laptop/. | 42 | * available to userspace under /sys/class/backlight/fujitsu-laptop/. |
41 | * | 43 | * |
42 | * This driver has been tested on a Fujitsu Lifebook S7020. It should | 44 | * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are |
43 | * work on most P-series and S-series Lifebooks, but YMMV. | 45 | * also supported by this driver. |
46 | * | ||
47 | * This driver has been tested on a Fujitsu Lifebook S6410 and S7020. It | ||
48 | * should work on most P-series and S-series Lifebooks, but YMMV. | ||
49 | * | ||
50 | * The module parameter use_alt_lcd_levels switches between different ACPI | ||
51 | * brightness controls which are used by different Fujitsu laptops. In most | ||
52 | * cases the correct method is automatically detected. "use_alt_lcd_levels=1" | ||
53 | * is applicable for a Fujitsu Lifebook S6410 if autodetection fails. | ||
54 | * | ||
44 | */ | 55 | */ |
45 | 56 | ||
46 | #include <linux/module.h> | 57 | #include <linux/module.h> |
@@ -49,30 +60,105 @@ | |||
49 | #include <linux/acpi.h> | 60 | #include <linux/acpi.h> |
50 | #include <linux/dmi.h> | 61 | #include <linux/dmi.h> |
51 | #include <linux/backlight.h> | 62 | #include <linux/backlight.h> |
63 | #include <linux/input.h> | ||
64 | #include <linux/kfifo.h> | ||
65 | #include <linux/video_output.h> | ||
52 | #include <linux/platform_device.h> | 66 | #include <linux/platform_device.h> |
53 | 67 | ||
54 | #define FUJITSU_DRIVER_VERSION "0.3" | 68 | #define FUJITSU_DRIVER_VERSION "0.4.2" |
55 | 69 | ||
56 | #define FUJITSU_LCD_N_LEVELS 8 | 70 | #define FUJITSU_LCD_N_LEVELS 8 |
57 | 71 | ||
58 | #define ACPI_FUJITSU_CLASS "fujitsu" | 72 | #define ACPI_FUJITSU_CLASS "fujitsu" |
59 | #define ACPI_FUJITSU_HID "FUJ02B1" | 73 | #define ACPI_FUJITSU_HID "FUJ02B1" |
60 | #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI extras driver" | 74 | #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver" |
61 | #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" | 75 | #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" |
62 | 76 | #define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3" | |
77 | #define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" | ||
78 | #define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3" | ||
79 | |||
80 | #define ACPI_FUJITSU_NOTIFY_CODE1 0x80 | ||
81 | |||
82 | #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 | ||
83 | #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 | ||
84 | |||
85 | /* Hotkey details */ | ||
86 | #define LOCK_KEY 0x410 /* codes for the keys in the GIRB register */ | ||
87 | #define DISPLAY_KEY 0x411 /* keys are mapped to KEY_SCREENLOCK (the key with the key symbol) */ | ||
88 | #define ENERGY_KEY 0x412 /* KEY_MEDIA (the key with the laptop symbol, KEY_EMAIL (E key)) */ | ||
89 | #define REST_KEY 0x413 /* KEY_SUSPEND (R key) */ | ||
90 | |||
91 | #define MAX_HOTKEY_RINGBUFFER_SIZE 100 | ||
92 | #define RINGBUFFERSIZE 40 | ||
93 | |||
94 | /* Debugging */ | ||
95 | #define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": " | ||
96 | #define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG | ||
97 | #define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG | ||
98 | #define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG | ||
99 | #define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG | ||
100 | |||
101 | #define FUJLAPTOP_DBG_ALL 0xffff | ||
102 | #define FUJLAPTOP_DBG_ERROR 0x0001 | ||
103 | #define FUJLAPTOP_DBG_WARN 0x0002 | ||
104 | #define FUJLAPTOP_DBG_INFO 0x0004 | ||
105 | #define FUJLAPTOP_DBG_TRACE 0x0008 | ||
106 | |||
107 | #define dbg_printk(a_dbg_level, format, arg...) \ | ||
108 | do { if (dbg_level & a_dbg_level) \ | ||
109 | printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ | ||
110 | } while (0) | ||
111 | #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG | ||
112 | #define vdbg_printk(a_dbg_level, format, arg...) \ | ||
113 | dbg_printk(a_dbg_level, format, ## arg) | ||
114 | #else | ||
115 | #define vdbg_printk(a_dbg_level, format, arg...) | ||
116 | #endif | ||
117 | |||
118 | /* Device controlling the backlight and associated keys */ | ||
63 | struct fujitsu_t { | 119 | struct fujitsu_t { |
64 | acpi_handle acpi_handle; | 120 | acpi_handle acpi_handle; |
121 | struct acpi_device *dev; | ||
122 | struct input_dev *input; | ||
123 | char phys[32]; | ||
65 | struct backlight_device *bl_device; | 124 | struct backlight_device *bl_device; |
66 | struct platform_device *pf_device; | 125 | struct platform_device *pf_device; |
67 | 126 | ||
68 | unsigned long fuj02b1_state; | 127 | unsigned int max_brightness; |
69 | unsigned int brightness_changed; | 128 | unsigned int brightness_changed; |
70 | unsigned int brightness_level; | 129 | unsigned int brightness_level; |
71 | }; | 130 | }; |
72 | 131 | ||
73 | static struct fujitsu_t *fujitsu; | 132 | static struct fujitsu_t *fujitsu; |
133 | static int use_alt_lcd_levels = -1; | ||
134 | static int disable_brightness_keys = -1; | ||
135 | static int disable_brightness_adjust = -1; | ||
136 | |||
137 | /* Device used to access other hotkeys on the laptop */ | ||
138 | struct fujitsu_hotkey_t { | ||
139 | acpi_handle acpi_handle; | ||
140 | struct acpi_device *dev; | ||
141 | struct input_dev *input; | ||
142 | char phys[32]; | ||
143 | struct platform_device *pf_device; | ||
144 | struct kfifo *fifo; | ||
145 | spinlock_t fifo_lock; | ||
146 | |||
147 | unsigned int irb; /* info about the pressed buttons */ | ||
148 | }; | ||
74 | 149 | ||
75 | /* Hardware access */ | 150 | static struct fujitsu_hotkey_t *fujitsu_hotkey; |
151 | |||
152 | static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, | ||
153 | void *data); | ||
154 | |||
155 | #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG | ||
156 | static u32 dbg_level = 0x03; | ||
157 | #endif | ||
158 | |||
159 | static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data); | ||
160 | |||
161 | /* Hardware access for LCD brightness control */ | ||
76 | 162 | ||
77 | static int set_lcd_level(int level) | 163 | static int set_lcd_level(int level) |
78 | { | 164 | { |
@@ -81,7 +167,10 @@ static int set_lcd_level(int level) | |||
81 | struct acpi_object_list arg_list = { 1, &arg0 }; | 167 | struct acpi_object_list arg_list = { 1, &arg0 }; |
82 | acpi_handle handle = NULL; | 168 | acpi_handle handle = NULL; |
83 | 169 | ||
84 | if (level < 0 || level >= FUJITSU_LCD_N_LEVELS) | 170 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n", |
171 | level); | ||
172 | |||
173 | if (level < 0 || level >= fujitsu->max_brightness) | ||
85 | return -EINVAL; | 174 | return -EINVAL; |
86 | 175 | ||
87 | if (!fujitsu) | 176 | if (!fujitsu) |
@@ -89,7 +178,38 @@ static int set_lcd_level(int level) | |||
89 | 178 | ||
90 | status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); | 179 | status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); |
91 | if (ACPI_FAILURE(status)) { | 180 | if (ACPI_FAILURE(status)) { |
92 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n")); | 181 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); |
182 | return -ENODEV; | ||
183 | } | ||
184 | |||
185 | arg0.integer.value = level; | ||
186 | |||
187 | status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); | ||
188 | if (ACPI_FAILURE(status)) | ||
189 | return -ENODEV; | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int set_lcd_level_alt(int level) | ||
195 | { | ||
196 | acpi_status status = AE_OK; | ||
197 | union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | ||
198 | struct acpi_object_list arg_list = { 1, &arg0 }; | ||
199 | acpi_handle handle = NULL; | ||
200 | |||
201 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n", | ||
202 | level); | ||
203 | |||
204 | if (level < 0 || level >= fujitsu->max_brightness) | ||
205 | return -EINVAL; | ||
206 | |||
207 | if (!fujitsu) | ||
208 | return -EINVAL; | ||
209 | |||
210 | status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); | ||
211 | if (ACPI_FAILURE(status)) { | ||
212 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); | ||
93 | return -ENODEV; | 213 | return -ENODEV; |
94 | } | 214 | } |
95 | 215 | ||
@@ -107,13 +227,52 @@ static int get_lcd_level(void) | |||
107 | unsigned long state = 0; | 227 | unsigned long state = 0; |
108 | acpi_status status = AE_OK; | 228 | acpi_status status = AE_OK; |
109 | 229 | ||
110 | // Get the Brightness | 230 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); |
231 | |||
111 | status = | 232 | status = |
112 | acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); | 233 | acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); |
113 | if (status < 0) | 234 | if (status < 0) |
114 | return status; | 235 | return status; |
115 | 236 | ||
116 | fujitsu->fuj02b1_state = state; | 237 | fujitsu->brightness_level = state & 0x0fffffff; |
238 | |||
239 | if (state & 0x80000000) | ||
240 | fujitsu->brightness_changed = 1; | ||
241 | else | ||
242 | fujitsu->brightness_changed = 0; | ||
243 | |||
244 | return fujitsu->brightness_level; | ||
245 | } | ||
246 | |||
247 | static int get_max_brightness(void) | ||
248 | { | ||
249 | unsigned long state = 0; | ||
250 | acpi_status status = AE_OK; | ||
251 | |||
252 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); | ||
253 | |||
254 | status = | ||
255 | acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state); | ||
256 | if (status < 0) | ||
257 | return status; | ||
258 | |||
259 | fujitsu->max_brightness = state; | ||
260 | |||
261 | return fujitsu->max_brightness; | ||
262 | } | ||
263 | |||
264 | static int get_lcd_level_alt(void) | ||
265 | { | ||
266 | unsigned long state = 0; | ||
267 | acpi_status status = AE_OK; | ||
268 | |||
269 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n"); | ||
270 | |||
271 | status = | ||
272 | acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state); | ||
273 | if (status < 0) | ||
274 | return status; | ||
275 | |||
117 | fujitsu->brightness_level = state & 0x0fffffff; | 276 | fujitsu->brightness_level = state & 0x0fffffff; |
118 | 277 | ||
119 | if (state & 0x80000000) | 278 | if (state & 0x80000000) |
@@ -128,12 +287,18 @@ static int get_lcd_level(void) | |||
128 | 287 | ||
129 | static int bl_get_brightness(struct backlight_device *b) | 288 | static int bl_get_brightness(struct backlight_device *b) |
130 | { | 289 | { |
131 | return get_lcd_level(); | 290 | if (use_alt_lcd_levels) |
291 | return get_lcd_level_alt(); | ||
292 | else | ||
293 | return get_lcd_level(); | ||
132 | } | 294 | } |
133 | 295 | ||
134 | static int bl_update_status(struct backlight_device *b) | 296 | static int bl_update_status(struct backlight_device *b) |
135 | { | 297 | { |
136 | return set_lcd_level(b->props.brightness); | 298 | if (use_alt_lcd_levels) |
299 | return set_lcd_level_alt(b->props.brightness); | ||
300 | else | ||
301 | return set_lcd_level(b->props.brightness); | ||
137 | } | 302 | } |
138 | 303 | ||
139 | static struct backlight_ops fujitsubl_ops = { | 304 | static struct backlight_ops fujitsubl_ops = { |
@@ -141,7 +306,35 @@ static struct backlight_ops fujitsubl_ops = { | |||
141 | .update_status = bl_update_status, | 306 | .update_status = bl_update_status, |
142 | }; | 307 | }; |
143 | 308 | ||
144 | /* Platform device */ | 309 | /* Platform LCD brightness device */ |
310 | |||
311 | static ssize_t | ||
312 | show_max_brightness(struct device *dev, | ||
313 | struct device_attribute *attr, char *buf) | ||
314 | { | ||
315 | |||
316 | int ret; | ||
317 | |||
318 | ret = get_max_brightness(); | ||
319 | if (ret < 0) | ||
320 | return ret; | ||
321 | |||
322 | return sprintf(buf, "%i\n", ret); | ||
323 | } | ||
324 | |||
325 | static ssize_t | ||
326 | show_brightness_changed(struct device *dev, | ||
327 | struct device_attribute *attr, char *buf) | ||
328 | { | ||
329 | |||
330 | int ret; | ||
331 | |||
332 | ret = fujitsu->brightness_changed; | ||
333 | if (ret < 0) | ||
334 | return ret; | ||
335 | |||
336 | return sprintf(buf, "%i\n", ret); | ||
337 | } | ||
145 | 338 | ||
146 | static ssize_t show_lcd_level(struct device *dev, | 339 | static ssize_t show_lcd_level(struct device *dev, |
147 | struct device_attribute *attr, char *buf) | 340 | struct device_attribute *attr, char *buf) |
@@ -149,7 +342,10 @@ static ssize_t show_lcd_level(struct device *dev, | |||
149 | 342 | ||
150 | int ret; | 343 | int ret; |
151 | 344 | ||
152 | ret = get_lcd_level(); | 345 | if (use_alt_lcd_levels) |
346 | ret = get_lcd_level_alt(); | ||
347 | else | ||
348 | ret = get_lcd_level(); | ||
153 | if (ret < 0) | 349 | if (ret < 0) |
154 | return ret; | 350 | return ret; |
155 | 351 | ||
@@ -164,19 +360,61 @@ static ssize_t store_lcd_level(struct device *dev, | |||
164 | int level, ret; | 360 | int level, ret; |
165 | 361 | ||
166 | if (sscanf(buf, "%i", &level) != 1 | 362 | if (sscanf(buf, "%i", &level) != 1 |
167 | || (level < 0 || level >= FUJITSU_LCD_N_LEVELS)) | 363 | || (level < 0 || level >= fujitsu->max_brightness)) |
168 | return -EINVAL; | 364 | return -EINVAL; |
169 | 365 | ||
170 | ret = set_lcd_level(level); | 366 | if (use_alt_lcd_levels) |
367 | ret = set_lcd_level_alt(level); | ||
368 | else | ||
369 | ret = set_lcd_level(level); | ||
370 | if (ret < 0) | ||
371 | return ret; | ||
372 | |||
373 | if (use_alt_lcd_levels) | ||
374 | ret = get_lcd_level_alt(); | ||
375 | else | ||
376 | ret = get_lcd_level(); | ||
171 | if (ret < 0) | 377 | if (ret < 0) |
172 | return ret; | 378 | return ret; |
173 | 379 | ||
174 | return count; | 380 | return count; |
175 | } | 381 | } |
176 | 382 | ||
383 | /* Hardware access for hotkey device */ | ||
384 | |||
385 | static int get_irb(void) | ||
386 | { | ||
387 | unsigned long state = 0; | ||
388 | acpi_status status = AE_OK; | ||
389 | |||
390 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n"); | ||
391 | |||
392 | status = | ||
393 | acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL, | ||
394 | &state); | ||
395 | if (status < 0) | ||
396 | return status; | ||
397 | |||
398 | fujitsu_hotkey->irb = state; | ||
399 | |||
400 | return fujitsu_hotkey->irb; | ||
401 | } | ||
402 | |||
403 | static ssize_t | ||
404 | ignore_store(struct device *dev, | ||
405 | struct device_attribute *attr, const char *buf, size_t count) | ||
406 | { | ||
407 | return count; | ||
408 | } | ||
409 | |||
410 | static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); | ||
411 | static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, | ||
412 | ignore_store); | ||
177 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); | 413 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); |
178 | 414 | ||
179 | static struct attribute *fujitsupf_attributes[] = { | 415 | static struct attribute *fujitsupf_attributes[] = { |
416 | &dev_attr_brightness_changed.attr, | ||
417 | &dev_attr_max_brightness.attr, | ||
180 | &dev_attr_lcd_level.attr, | 418 | &dev_attr_lcd_level.attr, |
181 | NULL | 419 | NULL |
182 | }; | 420 | }; |
@@ -192,14 +430,52 @@ static struct platform_driver fujitsupf_driver = { | |||
192 | } | 430 | } |
193 | }; | 431 | }; |
194 | 432 | ||
195 | /* ACPI device */ | 433 | static int dmi_check_cb_s6410(const struct dmi_system_id *id) |
434 | { | ||
435 | acpi_handle handle; | ||
436 | int have_blnf; | ||
437 | printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", | ||
438 | id->ident); | ||
439 | have_blnf = ACPI_SUCCESS | ||
440 | (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle)); | ||
441 | if (use_alt_lcd_levels == -1) { | ||
442 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n"); | ||
443 | use_alt_lcd_levels = 1; | ||
444 | } | ||
445 | if (disable_brightness_keys == -1) { | ||
446 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
447 | "auto-detecting disable_keys\n"); | ||
448 | disable_brightness_keys = have_blnf ? 1 : 0; | ||
449 | } | ||
450 | if (disable_brightness_adjust == -1) { | ||
451 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
452 | "auto-detecting disable_adjust\n"); | ||
453 | disable_brightness_adjust = have_blnf ? 0 : 1; | ||
454 | } | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static struct dmi_system_id __initdata fujitsu_dmi_table[] = { | ||
459 | { | ||
460 | .ident = "Fujitsu Siemens", | ||
461 | .matches = { | ||
462 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | ||
463 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"), | ||
464 | }, | ||
465 | .callback = dmi_check_cb_s6410}, | ||
466 | {} | ||
467 | }; | ||
468 | |||
469 | /* ACPI device for LCD brightness control */ | ||
196 | 470 | ||
197 | static int acpi_fujitsu_add(struct acpi_device *device) | 471 | static int acpi_fujitsu_add(struct acpi_device *device) |
198 | { | 472 | { |
473 | acpi_status status; | ||
474 | acpi_handle handle; | ||
199 | int result = 0; | 475 | int result = 0; |
200 | int state = 0; | 476 | int state = 0; |
201 | 477 | struct input_dev *input; | |
202 | ACPI_FUNCTION_TRACE("acpi_fujitsu_add"); | 478 | int error; |
203 | 479 | ||
204 | if (!device) | 480 | if (!device) |
205 | return -EINVAL; | 481 | return -EINVAL; |
@@ -209,10 +485,42 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
209 | sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); | 485 | sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); |
210 | acpi_driver_data(device) = fujitsu; | 486 | acpi_driver_data(device) = fujitsu; |
211 | 487 | ||
488 | status = acpi_install_notify_handler(device->handle, | ||
489 | ACPI_DEVICE_NOTIFY, | ||
490 | acpi_fujitsu_notify, fujitsu); | ||
491 | |||
492 | if (ACPI_FAILURE(status)) { | ||
493 | printk(KERN_ERR "Error installing notify handler\n"); | ||
494 | error = -ENODEV; | ||
495 | goto err_stop; | ||
496 | } | ||
497 | |||
498 | fujitsu->input = input = input_allocate_device(); | ||
499 | if (!input) { | ||
500 | error = -ENOMEM; | ||
501 | goto err_uninstall_notify; | ||
502 | } | ||
503 | |||
504 | snprintf(fujitsu->phys, sizeof(fujitsu->phys), | ||
505 | "%s/video/input0", acpi_device_hid(device)); | ||
506 | |||
507 | input->name = acpi_device_name(device); | ||
508 | input->phys = fujitsu->phys; | ||
509 | input->id.bustype = BUS_HOST; | ||
510 | input->id.product = 0x06; | ||
511 | input->dev.parent = &device->dev; | ||
512 | input->evbit[0] = BIT(EV_KEY); | ||
513 | set_bit(KEY_BRIGHTNESSUP, input->keybit); | ||
514 | set_bit(KEY_BRIGHTNESSDOWN, input->keybit); | ||
515 | set_bit(KEY_UNKNOWN, input->keybit); | ||
516 | |||
517 | error = input_register_device(input); | ||
518 | if (error) | ||
519 | goto err_free_input_dev; | ||
520 | |||
212 | result = acpi_bus_get_power(fujitsu->acpi_handle, &state); | 521 | result = acpi_bus_get_power(fujitsu->acpi_handle, &state); |
213 | if (result) { | 522 | if (result) { |
214 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 523 | printk(KERN_ERR "Error reading power state\n"); |
215 | "Error reading power state\n")); | ||
216 | goto end; | 524 | goto end; |
217 | } | 525 | } |
218 | 526 | ||
@@ -220,22 +528,373 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
220 | acpi_device_name(device), acpi_device_bid(device), | 528 | acpi_device_name(device), acpi_device_bid(device), |
221 | !device->power.state ? "on" : "off"); | 529 | !device->power.state ? "on" : "off"); |
222 | 530 | ||
223 | end: | 531 | fujitsu->dev = device; |
532 | |||
533 | if (ACPI_SUCCESS | ||
534 | (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { | ||
535 | vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); | ||
536 | if (ACPI_FAILURE | ||
537 | (acpi_evaluate_object | ||
538 | (device->handle, METHOD_NAME__INI, NULL, NULL))) | ||
539 | printk(KERN_ERR "_INI Method failed\n"); | ||
540 | } | ||
541 | |||
542 | /* do config (detect defaults) */ | ||
543 | dmi_check_system(fujitsu_dmi_table); | ||
544 | use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; | ||
545 | disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0; | ||
546 | disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; | ||
547 | vdbg_printk(FUJLAPTOP_DBG_INFO, | ||
548 | "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n", | ||
549 | use_alt_lcd_levels, disable_brightness_keys, | ||
550 | disable_brightness_adjust); | ||
551 | |||
552 | if (get_max_brightness() <= 0) | ||
553 | fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; | ||
554 | if (use_alt_lcd_levels) | ||
555 | get_lcd_level_alt(); | ||
556 | else | ||
557 | get_lcd_level(); | ||
558 | |||
559 | return result; | ||
560 | |||
561 | end: | ||
562 | err_free_input_dev: | ||
563 | input_free_device(input); | ||
564 | err_uninstall_notify: | ||
565 | acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, | ||
566 | acpi_fujitsu_notify); | ||
567 | err_stop: | ||
224 | 568 | ||
225 | return result; | 569 | return result; |
226 | } | 570 | } |
227 | 571 | ||
228 | static int acpi_fujitsu_remove(struct acpi_device *device, int type) | 572 | static int acpi_fujitsu_remove(struct acpi_device *device, int type) |
229 | { | 573 | { |
230 | ACPI_FUNCTION_TRACE("acpi_fujitsu_remove"); | 574 | acpi_status status; |
575 | struct fujitsu_t *fujitsu = NULL; | ||
231 | 576 | ||
232 | if (!device || !acpi_driver_data(device)) | 577 | if (!device || !acpi_driver_data(device)) |
233 | return -EINVAL; | 578 | return -EINVAL; |
579 | |||
580 | fujitsu = acpi_driver_data(device); | ||
581 | |||
582 | status = acpi_remove_notify_handler(fujitsu->acpi_handle, | ||
583 | ACPI_DEVICE_NOTIFY, | ||
584 | acpi_fujitsu_notify); | ||
585 | |||
586 | if (!device || !acpi_driver_data(device)) | ||
587 | return -EINVAL; | ||
588 | |||
234 | fujitsu->acpi_handle = NULL; | 589 | fujitsu->acpi_handle = NULL; |
235 | 590 | ||
236 | return 0; | 591 | return 0; |
237 | } | 592 | } |
238 | 593 | ||
594 | /* Brightness notify */ | ||
595 | |||
596 | static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) | ||
597 | { | ||
598 | struct input_dev *input; | ||
599 | int keycode; | ||
600 | int oldb, newb; | ||
601 | |||
602 | input = fujitsu->input; | ||
603 | |||
604 | switch (event) { | ||
605 | case ACPI_FUJITSU_NOTIFY_CODE1: | ||
606 | keycode = 0; | ||
607 | oldb = fujitsu->brightness_level; | ||
608 | get_lcd_level(); /* the alt version always yields changed */ | ||
609 | newb = fujitsu->brightness_level; | ||
610 | |||
611 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
612 | "brightness button event [%i -> %i (%i)]\n", | ||
613 | oldb, newb, fujitsu->brightness_changed); | ||
614 | |||
615 | if (oldb == newb && fujitsu->brightness_changed) { | ||
616 | keycode = 0; | ||
617 | if (disable_brightness_keys != 1) { | ||
618 | if (oldb == 0) { | ||
619 | acpi_bus_generate_proc_event(fujitsu-> | ||
620 | dev, | ||
621 | ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, | ||
622 | 0); | ||
623 | keycode = KEY_BRIGHTNESSDOWN; | ||
624 | } else if (oldb == | ||
625 | (fujitsu->max_brightness) - 1) { | ||
626 | acpi_bus_generate_proc_event(fujitsu-> | ||
627 | dev, | ||
628 | ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, | ||
629 | 0); | ||
630 | keycode = KEY_BRIGHTNESSUP; | ||
631 | } | ||
632 | } | ||
633 | } else if (oldb < newb) { | ||
634 | if (disable_brightness_adjust != 1) { | ||
635 | if (use_alt_lcd_levels) | ||
636 | set_lcd_level_alt(newb); | ||
637 | else | ||
638 | set_lcd_level(newb); | ||
639 | } | ||
640 | if (disable_brightness_keys != 1) { | ||
641 | acpi_bus_generate_proc_event(fujitsu->dev, | ||
642 | ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, | ||
643 | 0); | ||
644 | keycode = KEY_BRIGHTNESSUP; | ||
645 | } | ||
646 | } else if (oldb > newb) { | ||
647 | if (disable_brightness_adjust != 1) { | ||
648 | if (use_alt_lcd_levels) | ||
649 | set_lcd_level_alt(newb); | ||
650 | else | ||
651 | set_lcd_level(newb); | ||
652 | } | ||
653 | if (disable_brightness_keys != 1) { | ||
654 | acpi_bus_generate_proc_event(fujitsu->dev, | ||
655 | ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, | ||
656 | 0); | ||
657 | keycode = KEY_BRIGHTNESSDOWN; | ||
658 | } | ||
659 | } else { | ||
660 | keycode = KEY_UNKNOWN; | ||
661 | } | ||
662 | break; | ||
663 | default: | ||
664 | keycode = KEY_UNKNOWN; | ||
665 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
666 | "unsupported event [0x%x]\n", event); | ||
667 | break; | ||
668 | } | ||
669 | |||
670 | if (keycode != 0) { | ||
671 | input_report_key(input, keycode, 1); | ||
672 | input_sync(input); | ||
673 | input_report_key(input, keycode, 0); | ||
674 | input_sync(input); | ||
675 | } | ||
676 | |||
677 | return; | ||
678 | } | ||
679 | |||
680 | /* ACPI device for hotkey handling */ | ||
681 | |||
682 | static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | ||
683 | { | ||
684 | acpi_status status; | ||
685 | acpi_handle handle; | ||
686 | int result = 0; | ||
687 | int state = 0; | ||
688 | struct input_dev *input; | ||
689 | int error; | ||
690 | int i; | ||
691 | |||
692 | if (!device) | ||
693 | return -EINVAL; | ||
694 | |||
695 | fujitsu_hotkey->acpi_handle = device->handle; | ||
696 | sprintf(acpi_device_name(device), "%s", | ||
697 | ACPI_FUJITSU_HOTKEY_DEVICE_NAME); | ||
698 | sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); | ||
699 | acpi_driver_data(device) = fujitsu_hotkey; | ||
700 | |||
701 | status = acpi_install_notify_handler(device->handle, | ||
702 | ACPI_DEVICE_NOTIFY, | ||
703 | acpi_fujitsu_hotkey_notify, | ||
704 | fujitsu_hotkey); | ||
705 | |||
706 | if (ACPI_FAILURE(status)) { | ||
707 | printk(KERN_ERR "Error installing notify handler\n"); | ||
708 | error = -ENODEV; | ||
709 | goto err_stop; | ||
710 | } | ||
711 | |||
712 | /* kfifo */ | ||
713 | spin_lock_init(&fujitsu_hotkey->fifo_lock); | ||
714 | fujitsu_hotkey->fifo = | ||
715 | kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL, | ||
716 | &fujitsu_hotkey->fifo_lock); | ||
717 | if (IS_ERR(fujitsu_hotkey->fifo)) { | ||
718 | printk(KERN_ERR "kfifo_alloc failed\n"); | ||
719 | error = PTR_ERR(fujitsu_hotkey->fifo); | ||
720 | goto err_stop; | ||
721 | } | ||
722 | |||
723 | fujitsu_hotkey->input = input = input_allocate_device(); | ||
724 | if (!input) { | ||
725 | error = -ENOMEM; | ||
726 | goto err_uninstall_notify; | ||
727 | } | ||
728 | |||
729 | snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys), | ||
730 | "%s/video/input0", acpi_device_hid(device)); | ||
731 | |||
732 | input->name = acpi_device_name(device); | ||
733 | input->phys = fujitsu_hotkey->phys; | ||
734 | input->id.bustype = BUS_HOST; | ||
735 | input->id.product = 0x06; | ||
736 | input->dev.parent = &device->dev; | ||
737 | input->evbit[0] = BIT(EV_KEY); | ||
738 | set_bit(KEY_SCREENLOCK, input->keybit); | ||
739 | set_bit(KEY_MEDIA, input->keybit); | ||
740 | set_bit(KEY_EMAIL, input->keybit); | ||
741 | set_bit(KEY_SUSPEND, input->keybit); | ||
742 | set_bit(KEY_UNKNOWN, input->keybit); | ||
743 | |||
744 | error = input_register_device(input); | ||
745 | if (error) | ||
746 | goto err_free_input_dev; | ||
747 | |||
748 | result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); | ||
749 | if (result) { | ||
750 | printk(KERN_ERR "Error reading power state\n"); | ||
751 | goto end; | ||
752 | } | ||
753 | |||
754 | printk(KERN_INFO PREFIX "%s [%s] (%s)\n", | ||
755 | acpi_device_name(device), acpi_device_bid(device), | ||
756 | !device->power.state ? "on" : "off"); | ||
757 | |||
758 | fujitsu_hotkey->dev = device; | ||
759 | |||
760 | if (ACPI_SUCCESS | ||
761 | (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { | ||
762 | vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); | ||
763 | if (ACPI_FAILURE | ||
764 | (acpi_evaluate_object | ||
765 | (device->handle, METHOD_NAME__INI, NULL, NULL))) | ||
766 | printk(KERN_ERR "_INI Method failed\n"); | ||
767 | } | ||
768 | |||
769 | i = 0; /* Discard hotkey ringbuffer */ | ||
770 | while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ; | ||
771 | vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); | ||
772 | |||
773 | return result; | ||
774 | |||
775 | end: | ||
776 | err_free_input_dev: | ||
777 | input_free_device(input); | ||
778 | err_uninstall_notify: | ||
779 | acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, | ||
780 | acpi_fujitsu_hotkey_notify); | ||
781 | kfifo_free(fujitsu_hotkey->fifo); | ||
782 | err_stop: | ||
783 | |||
784 | return result; | ||
785 | } | ||
786 | |||
787 | static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) | ||
788 | { | ||
789 | acpi_status status; | ||
790 | struct fujitsu_hotkey_t *fujitsu_hotkey = NULL; | ||
791 | |||
792 | if (!device || !acpi_driver_data(device)) | ||
793 | return -EINVAL; | ||
794 | |||
795 | fujitsu_hotkey = acpi_driver_data(device); | ||
796 | |||
797 | status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle, | ||
798 | ACPI_DEVICE_NOTIFY, | ||
799 | acpi_fujitsu_hotkey_notify); | ||
800 | |||
801 | fujitsu_hotkey->acpi_handle = NULL; | ||
802 | |||
803 | kfifo_free(fujitsu_hotkey->fifo); | ||
804 | |||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, | ||
809 | void *data) | ||
810 | { | ||
811 | struct input_dev *input; | ||
812 | int keycode, keycode_r; | ||
813 | unsigned int irb = 1; | ||
814 | int i, status; | ||
815 | |||
816 | input = fujitsu_hotkey->input; | ||
817 | |||
818 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n"); | ||
819 | |||
820 | switch (event) { | ||
821 | case ACPI_FUJITSU_NOTIFY_CODE1: | ||
822 | i = 0; | ||
823 | while ((irb = get_irb()) != 0 | ||
824 | && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { | ||
825 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n", | ||
826 | irb); | ||
827 | |||
828 | switch (irb & 0x4ff) { | ||
829 | case LOCK_KEY: | ||
830 | keycode = KEY_SCREENLOCK; | ||
831 | break; | ||
832 | case DISPLAY_KEY: | ||
833 | keycode = KEY_MEDIA; | ||
834 | break; | ||
835 | case ENERGY_KEY: | ||
836 | keycode = KEY_EMAIL; | ||
837 | break; | ||
838 | case REST_KEY: | ||
839 | keycode = KEY_SUSPEND; | ||
840 | break; | ||
841 | case 0: | ||
842 | keycode = 0; | ||
843 | break; | ||
844 | default: | ||
845 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
846 | "Unknown GIRB result [%x]\n", irb); | ||
847 | keycode = -1; | ||
848 | break; | ||
849 | } | ||
850 | if (keycode > 0) { | ||
851 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
852 | "Push keycode into ringbuffer [%d]\n", | ||
853 | keycode); | ||
854 | status = kfifo_put(fujitsu_hotkey->fifo, | ||
855 | (unsigned char *)&keycode, | ||
856 | sizeof(keycode)); | ||
857 | if (status != sizeof(keycode)) { | ||
858 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
859 | "Could not push keycode [0x%x]\n", | ||
860 | keycode); | ||
861 | } else { | ||
862 | input_report_key(input, keycode, 1); | ||
863 | input_sync(input); | ||
864 | } | ||
865 | } else if (keycode == 0) { | ||
866 | while ((status = | ||
867 | kfifo_get | ||
868 | (fujitsu_hotkey->fifo, (unsigned char *) | ||
869 | &keycode_r, | ||
870 | sizeof | ||
871 | (keycode_r))) == sizeof(keycode_r)) { | ||
872 | input_report_key(input, keycode_r, 0); | ||
873 | input_sync(input); | ||
874 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
875 | "Pop keycode from ringbuffer [%d]\n", | ||
876 | keycode_r); | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | |||
881 | break; | ||
882 | default: | ||
883 | keycode = KEY_UNKNOWN; | ||
884 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
885 | "Unsupported event [0x%x]\n", event); | ||
886 | input_report_key(input, keycode, 1); | ||
887 | input_sync(input); | ||
888 | input_report_key(input, keycode, 0); | ||
889 | input_sync(input); | ||
890 | break; | ||
891 | } | ||
892 | |||
893 | return; | ||
894 | } | ||
895 | |||
896 | /* Initialization */ | ||
897 | |||
239 | static const struct acpi_device_id fujitsu_device_ids[] = { | 898 | static const struct acpi_device_id fujitsu_device_ids[] = { |
240 | {ACPI_FUJITSU_HID, 0}, | 899 | {ACPI_FUJITSU_HID, 0}, |
241 | {"", 0}, | 900 | {"", 0}, |
@@ -251,11 +910,24 @@ static struct acpi_driver acpi_fujitsu_driver = { | |||
251 | }, | 910 | }, |
252 | }; | 911 | }; |
253 | 912 | ||
254 | /* Initialization */ | 913 | static const struct acpi_device_id fujitsu_hotkey_device_ids[] = { |
914 | {ACPI_FUJITSU_HOTKEY_HID, 0}, | ||
915 | {"", 0}, | ||
916 | }; | ||
917 | |||
918 | static struct acpi_driver acpi_fujitsu_hotkey_driver = { | ||
919 | .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME, | ||
920 | .class = ACPI_FUJITSU_CLASS, | ||
921 | .ids = fujitsu_hotkey_device_ids, | ||
922 | .ops = { | ||
923 | .add = acpi_fujitsu_hotkey_add, | ||
924 | .remove = acpi_fujitsu_hotkey_remove, | ||
925 | }, | ||
926 | }; | ||
255 | 927 | ||
256 | static int __init fujitsu_init(void) | 928 | static int __init fujitsu_init(void) |
257 | { | 929 | { |
258 | int ret, result; | 930 | int ret, result, max_brightness; |
259 | 931 | ||
260 | if (acpi_disabled) | 932 | if (acpi_disabled) |
261 | return -ENODEV; | 933 | return -ENODEV; |
@@ -271,19 +943,6 @@ static int __init fujitsu_init(void) | |||
271 | goto fail_acpi; | 943 | goto fail_acpi; |
272 | } | 944 | } |
273 | 945 | ||
274 | /* Register backlight stuff */ | ||
275 | |||
276 | fujitsu->bl_device = | ||
277 | backlight_device_register("fujitsu-laptop", NULL, NULL, | ||
278 | &fujitsubl_ops); | ||
279 | if (IS_ERR(fujitsu->bl_device)) | ||
280 | return PTR_ERR(fujitsu->bl_device); | ||
281 | |||
282 | fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1; | ||
283 | ret = platform_driver_register(&fujitsupf_driver); | ||
284 | if (ret) | ||
285 | goto fail_backlight; | ||
286 | |||
287 | /* Register platform stuff */ | 946 | /* Register platform stuff */ |
288 | 947 | ||
289 | fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); | 948 | fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); |
@@ -302,28 +961,68 @@ static int __init fujitsu_init(void) | |||
302 | if (ret) | 961 | if (ret) |
303 | goto fail_platform_device2; | 962 | goto fail_platform_device2; |
304 | 963 | ||
964 | /* Register backlight stuff */ | ||
965 | |||
966 | fujitsu->bl_device = | ||
967 | backlight_device_register("fujitsu-laptop", NULL, NULL, | ||
968 | &fujitsubl_ops); | ||
969 | if (IS_ERR(fujitsu->bl_device)) | ||
970 | return PTR_ERR(fujitsu->bl_device); | ||
971 | |||
972 | max_brightness = fujitsu->max_brightness; | ||
973 | |||
974 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | ||
975 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; | ||
976 | |||
977 | ret = platform_driver_register(&fujitsupf_driver); | ||
978 | if (ret) | ||
979 | goto fail_backlight; | ||
980 | |||
981 | /* Register hotkey driver */ | ||
982 | |||
983 | fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); | ||
984 | if (!fujitsu_hotkey) { | ||
985 | ret = -ENOMEM; | ||
986 | goto fail_hotkey; | ||
987 | } | ||
988 | memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t)); | ||
989 | |||
990 | result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); | ||
991 | if (result < 0) { | ||
992 | ret = -ENODEV; | ||
993 | goto fail_hotkey1; | ||
994 | } | ||
995 | |||
305 | printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION | 996 | printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION |
306 | " successfully loaded.\n"); | 997 | " successfully loaded.\n"); |
307 | 998 | ||
308 | return 0; | 999 | return 0; |
309 | 1000 | ||
310 | fail_platform_device2: | 1001 | fail_hotkey1: |
311 | 1002 | ||
312 | platform_device_del(fujitsu->pf_device); | 1003 | kfree(fujitsu_hotkey); |
313 | 1004 | ||
314 | fail_platform_device1: | 1005 | fail_hotkey: |
315 | |||
316 | platform_device_put(fujitsu->pf_device); | ||
317 | |||
318 | fail_platform_driver: | ||
319 | 1006 | ||
320 | platform_driver_unregister(&fujitsupf_driver); | 1007 | platform_driver_unregister(&fujitsupf_driver); |
321 | 1008 | ||
322 | fail_backlight: | 1009 | fail_backlight: |
323 | 1010 | ||
324 | backlight_device_unregister(fujitsu->bl_device); | 1011 | backlight_device_unregister(fujitsu->bl_device); |
325 | 1012 | ||
326 | fail_acpi: | 1013 | fail_platform_device2: |
1014 | |||
1015 | platform_device_del(fujitsu->pf_device); | ||
1016 | |||
1017 | fail_platform_device1: | ||
1018 | |||
1019 | platform_device_put(fujitsu->pf_device); | ||
1020 | |||
1021 | fail_platform_driver: | ||
1022 | |||
1023 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | ||
1024 | |||
1025 | fail_acpi: | ||
327 | 1026 | ||
328 | kfree(fujitsu); | 1027 | kfree(fujitsu); |
329 | 1028 | ||
@@ -342,13 +1041,43 @@ static void __exit fujitsu_cleanup(void) | |||
342 | 1041 | ||
343 | kfree(fujitsu); | 1042 | kfree(fujitsu); |
344 | 1043 | ||
1044 | acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); | ||
1045 | |||
1046 | kfree(fujitsu_hotkey); | ||
1047 | |||
345 | printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); | 1048 | printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); |
346 | } | 1049 | } |
347 | 1050 | ||
348 | module_init(fujitsu_init); | 1051 | module_init(fujitsu_init); |
349 | module_exit(fujitsu_cleanup); | 1052 | module_exit(fujitsu_cleanup); |
350 | 1053 | ||
351 | MODULE_AUTHOR("Jonathan Woithe"); | 1054 | module_param(use_alt_lcd_levels, uint, 0644); |
1055 | MODULE_PARM_DESC(use_alt_lcd_levels, | ||
1056 | "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); | ||
1057 | module_param(disable_brightness_keys, uint, 0644); | ||
1058 | MODULE_PARM_DESC(disable_brightness_keys, | ||
1059 | "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device)."); | ||
1060 | module_param(disable_brightness_adjust, uint, 0644); | ||
1061 | MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); | ||
1062 | #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG | ||
1063 | module_param_named(debug, dbg_level, uint, 0644); | ||
1064 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); | ||
1065 | #endif | ||
1066 | |||
1067 | MODULE_AUTHOR("Jonathan Woithe, Peter Gruber"); | ||
352 | MODULE_DESCRIPTION("Fujitsu laptop extras support"); | 1068 | MODULE_DESCRIPTION("Fujitsu laptop extras support"); |
353 | MODULE_VERSION(FUJITSU_DRIVER_VERSION); | 1069 | MODULE_VERSION(FUJITSU_DRIVER_VERSION); |
354 | MODULE_LICENSE("GPL"); | 1070 | MODULE_LICENSE("GPL"); |
1071 | |||
1072 | MODULE_ALIAS | ||
1073 | ("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); | ||
1074 | MODULE_ALIAS | ||
1075 | ("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); | ||
1076 | |||
1077 | static struct pnp_device_id pnp_ids[] = { | ||
1078 | { .id = "FUJ02bf" }, | ||
1079 | { .id = "FUJ02B1" }, | ||
1080 | { .id = "FUJ02E3" }, | ||
1081 | { .id = "" } | ||
1082 | }; | ||
1083 | MODULE_DEVICE_TABLE(pnp, pnp_ids); | ||
diff --git a/drivers/misc/hdpuftrs/hdpu_cpustate.c b/drivers/misc/hdpuftrs/hdpu_cpustate.c index ff51ab67231c..176fe4e09d3f 100644 --- a/drivers/misc/hdpuftrs/hdpu_cpustate.c +++ b/drivers/misc/hdpuftrs/hdpu_cpustate.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/spinlock.h> | 19 | #include <linux/spinlock.h> |
20 | #include <linux/smp_lock.h> | ||
20 | #include <linux/miscdevice.h> | 21 | #include <linux/miscdevice.h> |
21 | #include <linux/proc_fs.h> | 22 | #include <linux/proc_fs.h> |
22 | #include <linux/hdpu_features.h> | 23 | #include <linux/hdpu_features.h> |
@@ -151,7 +152,13 @@ static ssize_t cpustate_write(struct file *file, const char *buf, | |||
151 | 152 | ||
152 | static int cpustate_open(struct inode *inode, struct file *file) | 153 | static int cpustate_open(struct inode *inode, struct file *file) |
153 | { | 154 | { |
154 | return cpustate_get_ref((file->f_flags & O_EXCL)); | 155 | int ret; |
156 | |||
157 | lock_kernel(); | ||
158 | ret = cpustate_get_ref((file->f_flags & O_EXCL)); | ||
159 | unlock_kernel(); | ||
160 | |||
161 | return ret; | ||
155 | } | 162 | } |
156 | 163 | ||
157 | static int cpustate_release(struct inode *inode, struct file *file) | 164 | static int cpustate_release(struct inode *inode, struct file *file) |
diff --git a/drivers/misc/hp-wmi.c b/drivers/misc/hp-wmi.c new file mode 100644 index 000000000000..1dbcbcb323a2 --- /dev/null +++ b/drivers/misc/hp-wmi.c | |||
@@ -0,0 +1,494 @@ | |||
1 | /* | ||
2 | * HP WMI hotkeys | ||
3 | * | ||
4 | * Copyright (C) 2008 Red Hat <mjg@redhat.com> | ||
5 | * | ||
6 | * Portions based on wistron_btns.c: | ||
7 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | ||
8 | * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | ||
9 | * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/input.h> | ||
31 | #include <acpi/acpi_drivers.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/acpi.h> | ||
34 | #include <linux/rfkill.h> | ||
35 | #include <linux/string.h> | ||
36 | |||
37 | MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>"); | ||
38 | MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | |||
41 | MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); | ||
42 | MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | ||
43 | |||
44 | #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" | ||
45 | #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" | ||
46 | |||
47 | #define HPWMI_DISPLAY_QUERY 0x1 | ||
48 | #define HPWMI_HDDTEMP_QUERY 0x2 | ||
49 | #define HPWMI_ALS_QUERY 0x3 | ||
50 | #define HPWMI_DOCK_QUERY 0x4 | ||
51 | #define HPWMI_WIRELESS_QUERY 0x5 | ||
52 | |||
53 | static int __init hp_wmi_bios_setup(struct platform_device *device); | ||
54 | static int __exit hp_wmi_bios_remove(struct platform_device *device); | ||
55 | |||
56 | struct bios_args { | ||
57 | u32 signature; | ||
58 | u32 command; | ||
59 | u32 commandtype; | ||
60 | u32 datasize; | ||
61 | u32 data; | ||
62 | }; | ||
63 | |||
64 | struct bios_return { | ||
65 | u32 sigpass; | ||
66 | u32 return_code; | ||
67 | u32 value; | ||
68 | }; | ||
69 | |||
70 | struct key_entry { | ||
71 | char type; /* See KE_* below */ | ||
72 | u8 code; | ||
73 | u16 keycode; | ||
74 | }; | ||
75 | |||
76 | enum { KE_KEY, KE_SW, KE_END }; | ||
77 | |||
78 | static struct key_entry hp_wmi_keymap[] = { | ||
79 | {KE_SW, 0x01, SW_DOCK}, | ||
80 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | ||
81 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | ||
82 | {KE_KEY, 0x04, KEY_HELP}, | ||
83 | {KE_END, 0} | ||
84 | }; | ||
85 | |||
86 | static struct input_dev *hp_wmi_input_dev; | ||
87 | static struct platform_device *hp_wmi_platform_dev; | ||
88 | |||
89 | static struct rfkill *wifi_rfkill; | ||
90 | static struct rfkill *bluetooth_rfkill; | ||
91 | static struct rfkill *wwan_rfkill; | ||
92 | |||
93 | static struct platform_driver hp_wmi_driver = { | ||
94 | .driver = { | ||
95 | .name = "hp-wmi", | ||
96 | .owner = THIS_MODULE, | ||
97 | }, | ||
98 | .probe = hp_wmi_bios_setup, | ||
99 | .remove = hp_wmi_bios_remove, | ||
100 | }; | ||
101 | |||
102 | static int hp_wmi_perform_query(int query, int write, int value) | ||
103 | { | ||
104 | struct bios_return bios_return; | ||
105 | acpi_status status; | ||
106 | union acpi_object *obj; | ||
107 | struct bios_args args = { | ||
108 | .signature = 0x55434553, | ||
109 | .command = write ? 0x2 : 0x1, | ||
110 | .commandtype = query, | ||
111 | .datasize = write ? 0x4 : 0, | ||
112 | .data = value, | ||
113 | }; | ||
114 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; | ||
115 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
116 | |||
117 | status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); | ||
118 | |||
119 | obj = output.pointer; | ||
120 | |||
121 | if (!obj || obj->type != ACPI_TYPE_BUFFER) | ||
122 | return -EINVAL; | ||
123 | |||
124 | bios_return = *((struct bios_return *)obj->buffer.pointer); | ||
125 | if (bios_return.return_code > 0) | ||
126 | return bios_return.return_code * -1; | ||
127 | else | ||
128 | return bios_return.value; | ||
129 | } | ||
130 | |||
131 | static int hp_wmi_display_state(void) | ||
132 | { | ||
133 | return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0); | ||
134 | } | ||
135 | |||
136 | static int hp_wmi_hddtemp_state(void) | ||
137 | { | ||
138 | return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0); | ||
139 | } | ||
140 | |||
141 | static int hp_wmi_als_state(void) | ||
142 | { | ||
143 | return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0); | ||
144 | } | ||
145 | |||
146 | static int hp_wmi_dock_state(void) | ||
147 | { | ||
148 | return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); | ||
149 | } | ||
150 | |||
151 | static int hp_wmi_wifi_set(void *data, enum rfkill_state state) | ||
152 | { | ||
153 | if (state) | ||
154 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101); | ||
155 | else | ||
156 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100); | ||
157 | } | ||
158 | |||
159 | static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state) | ||
160 | { | ||
161 | if (state) | ||
162 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202); | ||
163 | else | ||
164 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200); | ||
165 | } | ||
166 | |||
167 | static int hp_wmi_wwan_set(void *data, enum rfkill_state state) | ||
168 | { | ||
169 | if (state) | ||
170 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404); | ||
171 | else | ||
172 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400); | ||
173 | } | ||
174 | |||
175 | static int hp_wmi_wifi_state(void) | ||
176 | { | ||
177 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
178 | |||
179 | if (wireless & 0x100) | ||
180 | return 1; | ||
181 | else | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int hp_wmi_bluetooth_state(void) | ||
186 | { | ||
187 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
188 | |||
189 | if (wireless & 0x10000) | ||
190 | return 1; | ||
191 | else | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int hp_wmi_wwan_state(void) | ||
196 | { | ||
197 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
198 | |||
199 | if (wireless & 0x1000000) | ||
200 | return 1; | ||
201 | else | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, | ||
206 | char *buf) | ||
207 | { | ||
208 | int value = hp_wmi_display_state(); | ||
209 | if (value < 0) | ||
210 | return -EINVAL; | ||
211 | return sprintf(buf, "%d\n", value); | ||
212 | } | ||
213 | |||
214 | static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr, | ||
215 | char *buf) | ||
216 | { | ||
217 | int value = hp_wmi_hddtemp_state(); | ||
218 | if (value < 0) | ||
219 | return -EINVAL; | ||
220 | return sprintf(buf, "%d\n", value); | ||
221 | } | ||
222 | |||
223 | static ssize_t show_als(struct device *dev, struct device_attribute *attr, | ||
224 | char *buf) | ||
225 | { | ||
226 | int value = hp_wmi_als_state(); | ||
227 | if (value < 0) | ||
228 | return -EINVAL; | ||
229 | return sprintf(buf, "%d\n", value); | ||
230 | } | ||
231 | |||
232 | static ssize_t show_dock(struct device *dev, struct device_attribute *attr, | ||
233 | char *buf) | ||
234 | { | ||
235 | int value = hp_wmi_dock_state(); | ||
236 | if (value < 0) | ||
237 | return -EINVAL; | ||
238 | return sprintf(buf, "%d\n", value); | ||
239 | } | ||
240 | |||
241 | static ssize_t set_als(struct device *dev, struct device_attribute *attr, | ||
242 | const char *buf, size_t count) | ||
243 | { | ||
244 | u32 tmp = simple_strtoul(buf, NULL, 10); | ||
245 | hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp); | ||
246 | return count; | ||
247 | } | ||
248 | |||
249 | static DEVICE_ATTR(display, S_IRUGO, show_display, NULL); | ||
250 | static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); | ||
251 | static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | ||
252 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | ||
253 | |||
254 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) | ||
255 | { | ||
256 | struct key_entry *key; | ||
257 | |||
258 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
259 | if (code == key->code) | ||
260 | return key; | ||
261 | |||
262 | return NULL; | ||
263 | } | ||
264 | |||
265 | static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) | ||
266 | { | ||
267 | struct key_entry *key; | ||
268 | |||
269 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
270 | if (key->type == KE_KEY && keycode == key->keycode) | ||
271 | return key; | ||
272 | |||
273 | return NULL; | ||
274 | } | ||
275 | |||
276 | static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) | ||
277 | { | ||
278 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); | ||
279 | |||
280 | if (key && key->type == KE_KEY) { | ||
281 | *keycode = key->keycode; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) | ||
289 | { | ||
290 | struct key_entry *key; | ||
291 | int old_keycode; | ||
292 | |||
293 | if (keycode < 0 || keycode > KEY_MAX) | ||
294 | return -EINVAL; | ||
295 | |||
296 | key = hp_wmi_get_entry_by_scancode(scancode); | ||
297 | if (key && key->type == KE_KEY) { | ||
298 | old_keycode = key->keycode; | ||
299 | key->keycode = keycode; | ||
300 | set_bit(keycode, dev->keybit); | ||
301 | if (!hp_wmi_get_entry_by_keycode(old_keycode)) | ||
302 | clear_bit(old_keycode, dev->keybit); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | return -EINVAL; | ||
307 | } | ||
308 | |||
309 | void hp_wmi_notify(u32 value, void *context) | ||
310 | { | ||
311 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
312 | static struct key_entry *key; | ||
313 | union acpi_object *obj; | ||
314 | |||
315 | wmi_get_event_data(value, &response); | ||
316 | |||
317 | obj = (union acpi_object *)response.pointer; | ||
318 | |||
319 | if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) { | ||
320 | int eventcode = *((u8 *) obj->buffer.pointer); | ||
321 | key = hp_wmi_get_entry_by_scancode(eventcode); | ||
322 | if (key) { | ||
323 | switch (key->type) { | ||
324 | case KE_KEY: | ||
325 | input_report_key(hp_wmi_input_dev, | ||
326 | key->keycode, 1); | ||
327 | input_sync(hp_wmi_input_dev); | ||
328 | input_report_key(hp_wmi_input_dev, | ||
329 | key->keycode, 0); | ||
330 | input_sync(hp_wmi_input_dev); | ||
331 | break; | ||
332 | case KE_SW: | ||
333 | input_report_switch(hp_wmi_input_dev, | ||
334 | key->keycode, | ||
335 | hp_wmi_dock_state()); | ||
336 | input_sync(hp_wmi_input_dev); | ||
337 | break; | ||
338 | } | ||
339 | } else if (eventcode == 0x5) { | ||
340 | if (wifi_rfkill) | ||
341 | wifi_rfkill->state = hp_wmi_wifi_state(); | ||
342 | if (bluetooth_rfkill) | ||
343 | bluetooth_rfkill->state = | ||
344 | hp_wmi_bluetooth_state(); | ||
345 | if (wwan_rfkill) | ||
346 | wwan_rfkill->state = hp_wmi_wwan_state(); | ||
347 | } else | ||
348 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", | ||
349 | eventcode); | ||
350 | } else | ||
351 | printk(KERN_INFO "HP WMI: Unknown response received\n"); | ||
352 | } | ||
353 | |||
354 | static int __init hp_wmi_input_setup(void) | ||
355 | { | ||
356 | struct key_entry *key; | ||
357 | int err; | ||
358 | |||
359 | hp_wmi_input_dev = input_allocate_device(); | ||
360 | |||
361 | hp_wmi_input_dev->name = "HP WMI hotkeys"; | ||
362 | hp_wmi_input_dev->phys = "wmi/input0"; | ||
363 | hp_wmi_input_dev->id.bustype = BUS_HOST; | ||
364 | hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; | ||
365 | hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; | ||
366 | |||
367 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | ||
368 | switch (key->type) { | ||
369 | case KE_KEY: | ||
370 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | ||
371 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | ||
372 | break; | ||
373 | case KE_SW: | ||
374 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | ||
375 | set_bit(key->keycode, hp_wmi_input_dev->swbit); | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | err = input_register_device(hp_wmi_input_dev); | ||
381 | |||
382 | if (err) { | ||
383 | input_free_device(hp_wmi_input_dev); | ||
384 | return err; | ||
385 | } | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static void cleanup_sysfs(struct platform_device *device) | ||
391 | { | ||
392 | device_remove_file(&device->dev, &dev_attr_display); | ||
393 | device_remove_file(&device->dev, &dev_attr_hddtemp); | ||
394 | device_remove_file(&device->dev, &dev_attr_als); | ||
395 | device_remove_file(&device->dev, &dev_attr_dock); | ||
396 | } | ||
397 | |||
398 | static int __init hp_wmi_bios_setup(struct platform_device *device) | ||
399 | { | ||
400 | int err; | ||
401 | |||
402 | err = device_create_file(&device->dev, &dev_attr_display); | ||
403 | if (err) | ||
404 | goto add_sysfs_error; | ||
405 | err = device_create_file(&device->dev, &dev_attr_hddtemp); | ||
406 | if (err) | ||
407 | goto add_sysfs_error; | ||
408 | err = device_create_file(&device->dev, &dev_attr_als); | ||
409 | if (err) | ||
410 | goto add_sysfs_error; | ||
411 | err = device_create_file(&device->dev, &dev_attr_dock); | ||
412 | if (err) | ||
413 | goto add_sysfs_error; | ||
414 | |||
415 | wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); | ||
416 | wifi_rfkill->name = "hp-wifi"; | ||
417 | wifi_rfkill->state = hp_wmi_wifi_state(); | ||
418 | wifi_rfkill->toggle_radio = hp_wmi_wifi_set; | ||
419 | wifi_rfkill->user_claim_unsupported = 1; | ||
420 | |||
421 | bluetooth_rfkill = rfkill_allocate(&device->dev, | ||
422 | RFKILL_TYPE_BLUETOOTH); | ||
423 | bluetooth_rfkill->name = "hp-bluetooth"; | ||
424 | bluetooth_rfkill->state = hp_wmi_bluetooth_state(); | ||
425 | bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; | ||
426 | bluetooth_rfkill->user_claim_unsupported = 1; | ||
427 | |||
428 | wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); | ||
429 | wwan_rfkill->name = "hp-wwan"; | ||
430 | wwan_rfkill->state = hp_wmi_wwan_state(); | ||
431 | wwan_rfkill->toggle_radio = hp_wmi_wwan_set; | ||
432 | wwan_rfkill->user_claim_unsupported = 1; | ||
433 | |||
434 | rfkill_register(wifi_rfkill); | ||
435 | rfkill_register(bluetooth_rfkill); | ||
436 | rfkill_register(wwan_rfkill); | ||
437 | |||
438 | return 0; | ||
439 | add_sysfs_error: | ||
440 | cleanup_sysfs(device); | ||
441 | return err; | ||
442 | } | ||
443 | |||
444 | static int __exit hp_wmi_bios_remove(struct platform_device *device) | ||
445 | { | ||
446 | cleanup_sysfs(device); | ||
447 | |||
448 | rfkill_unregister(wifi_rfkill); | ||
449 | rfkill_unregister(bluetooth_rfkill); | ||
450 | rfkill_unregister(wwan_rfkill); | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | static int __init hp_wmi_init(void) | ||
456 | { | ||
457 | int err; | ||
458 | |||
459 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | ||
460 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | ||
461 | hp_wmi_notify, NULL); | ||
462 | if (!err) | ||
463 | hp_wmi_input_setup(); | ||
464 | } | ||
465 | |||
466 | if (wmi_has_guid(HPWMI_BIOS_GUID)) { | ||
467 | err = platform_driver_register(&hp_wmi_driver); | ||
468 | if (err) | ||
469 | return 0; | ||
470 | hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); | ||
471 | if (!hp_wmi_platform_dev) { | ||
472 | platform_driver_unregister(&hp_wmi_driver); | ||
473 | return 0; | ||
474 | } | ||
475 | platform_device_add(hp_wmi_platform_dev); | ||
476 | } | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static void __exit hp_wmi_exit(void) | ||
482 | { | ||
483 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | ||
484 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
485 | input_unregister_device(hp_wmi_input_dev); | ||
486 | } | ||
487 | if (hp_wmi_platform_dev) { | ||
488 | platform_device_del(hp_wmi_platform_dev); | ||
489 | platform_driver_unregister(&hp_wmi_driver); | ||
490 | } | ||
491 | } | ||
492 | |||
493 | module_init(hp_wmi_init); | ||
494 | module_exit(hp_wmi_exit); | ||
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c new file mode 100644 index 000000000000..05e298289238 --- /dev/null +++ b/drivers/misc/hpilo.c | |||
@@ -0,0 +1,768 @@ | |||
1 | /* | ||
2 | * Driver for HP iLO/iLO2 management processor. | ||
3 | * | ||
4 | * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. | ||
5 | * David Altobelli <david.altobelli@hp.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/ioport.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/file.h> | ||
19 | #include <linux/cdev.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | #include <linux/io.h> | ||
24 | #include "hpilo.h" | ||
25 | |||
26 | static struct class *ilo_class; | ||
27 | static unsigned int ilo_major; | ||
28 | static char ilo_hwdev[MAX_ILO_DEV]; | ||
29 | |||
30 | static inline int get_entry_id(int entry) | ||
31 | { | ||
32 | return (entry & ENTRY_MASK_DESCRIPTOR) >> ENTRY_BITPOS_DESCRIPTOR; | ||
33 | } | ||
34 | |||
35 | static inline int get_entry_len(int entry) | ||
36 | { | ||
37 | return ((entry & ENTRY_MASK_QWORDS) >> ENTRY_BITPOS_QWORDS) << 3; | ||
38 | } | ||
39 | |||
40 | static inline int mk_entry(int id, int len) | ||
41 | { | ||
42 | int qlen = len & 7 ? (len >> 3) + 1 : len >> 3; | ||
43 | return id << ENTRY_BITPOS_DESCRIPTOR | qlen << ENTRY_BITPOS_QWORDS; | ||
44 | } | ||
45 | |||
46 | static inline int desc_mem_sz(int nr_entry) | ||
47 | { | ||
48 | return nr_entry << L2_QENTRY_SZ; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * FIFO queues, shared with hardware. | ||
53 | * | ||
54 | * If a queue has empty slots, an entry is added to the queue tail, | ||
55 | * and that entry is marked as occupied. | ||
56 | * Entries can be dequeued from the head of the list, when the device | ||
57 | * has marked the entry as consumed. | ||
58 | * | ||
59 | * Returns true on successful queue/dequeue, false on failure. | ||
60 | */ | ||
61 | static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | ||
62 | { | ||
63 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | ||
64 | int ret = 0; | ||
65 | |||
66 | spin_lock(&hw->fifo_lock); | ||
67 | if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] | ||
68 | & ENTRY_MASK_O)) { | ||
69 | fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= | ||
70 | (entry & ENTRY_MASK_NOSTATE) | fifo_q->merge; | ||
71 | fifo_q->tail += 1; | ||
72 | ret = 1; | ||
73 | } | ||
74 | spin_unlock(&hw->fifo_lock); | ||
75 | |||
76 | return ret; | ||
77 | } | ||
78 | |||
79 | static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) | ||
80 | { | ||
81 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | ||
82 | int ret = 0; | ||
83 | u64 c; | ||
84 | |||
85 | spin_lock(&hw->fifo_lock); | ||
86 | c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; | ||
87 | if (c & ENTRY_MASK_C) { | ||
88 | if (entry) | ||
89 | *entry = c & ENTRY_MASK_NOSTATE; | ||
90 | |||
91 | fifo_q->fifobar[fifo_q->head & fifo_q->imask] = | ||
92 | (c | ENTRY_MASK) + 1; | ||
93 | fifo_q->head += 1; | ||
94 | ret = 1; | ||
95 | } | ||
96 | spin_unlock(&hw->fifo_lock); | ||
97 | |||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | static int ilo_pkt_enqueue(struct ilo_hwinfo *hw, struct ccb *ccb, | ||
102 | int dir, int id, int len) | ||
103 | { | ||
104 | char *fifobar; | ||
105 | int entry; | ||
106 | |||
107 | if (dir == SENDQ) | ||
108 | fifobar = ccb->ccb_u1.send_fifobar; | ||
109 | else | ||
110 | fifobar = ccb->ccb_u3.recv_fifobar; | ||
111 | |||
112 | entry = mk_entry(id, len); | ||
113 | return fifo_enqueue(hw, fifobar, entry); | ||
114 | } | ||
115 | |||
116 | static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, | ||
117 | int dir, int *id, int *len, void **pkt) | ||
118 | { | ||
119 | char *fifobar, *desc; | ||
120 | int entry = 0, pkt_id = 0; | ||
121 | int ret; | ||
122 | |||
123 | if (dir == SENDQ) { | ||
124 | fifobar = ccb->ccb_u1.send_fifobar; | ||
125 | desc = ccb->ccb_u2.send_desc; | ||
126 | } else { | ||
127 | fifobar = ccb->ccb_u3.recv_fifobar; | ||
128 | desc = ccb->ccb_u4.recv_desc; | ||
129 | } | ||
130 | |||
131 | ret = fifo_dequeue(hw, fifobar, &entry); | ||
132 | if (ret) { | ||
133 | pkt_id = get_entry_id(entry); | ||
134 | if (id) | ||
135 | *id = pkt_id; | ||
136 | if (len) | ||
137 | *len = get_entry_len(entry); | ||
138 | if (pkt) | ||
139 | *pkt = (void *)(desc + desc_mem_sz(pkt_id)); | ||
140 | } | ||
141 | |||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | static inline void doorbell_set(struct ccb *ccb) | ||
146 | { | ||
147 | iowrite8(1, ccb->ccb_u5.db_base); | ||
148 | } | ||
149 | |||
150 | static inline void doorbell_clr(struct ccb *ccb) | ||
151 | { | ||
152 | iowrite8(2, ccb->ccb_u5.db_base); | ||
153 | } | ||
154 | static inline int ctrl_set(int l2sz, int idxmask, int desclim) | ||
155 | { | ||
156 | int active = 0, go = 1; | ||
157 | return l2sz << CTRL_BITPOS_L2SZ | | ||
158 | idxmask << CTRL_BITPOS_FIFOINDEXMASK | | ||
159 | desclim << CTRL_BITPOS_DESCLIMIT | | ||
160 | active << CTRL_BITPOS_A | | ||
161 | go << CTRL_BITPOS_G; | ||
162 | } | ||
163 | static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) | ||
164 | { | ||
165 | /* for simplicity, use the same parameters for send and recv ctrls */ | ||
166 | ccb->send_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1); | ||
167 | ccb->recv_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1); | ||
168 | } | ||
169 | |||
170 | static inline int fifo_sz(int nr_entry) | ||
171 | { | ||
172 | /* size of a fifo is determined by the number of entries it contains */ | ||
173 | return (nr_entry * sizeof(u64)) + FIFOHANDLESIZE; | ||
174 | } | ||
175 | |||
176 | static void fifo_setup(void *base_addr, int nr_entry) | ||
177 | { | ||
178 | struct fifo *fifo_q = base_addr; | ||
179 | int i; | ||
180 | |||
181 | /* set up an empty fifo */ | ||
182 | fifo_q->head = 0; | ||
183 | fifo_q->tail = 0; | ||
184 | fifo_q->reset = 0; | ||
185 | fifo_q->nrents = nr_entry; | ||
186 | fifo_q->imask = nr_entry - 1; | ||
187 | fifo_q->merge = ENTRY_MASK_O; | ||
188 | |||
189 | for (i = 0; i < nr_entry; i++) | ||
190 | fifo_q->fifobar[i] = 0; | ||
191 | } | ||
192 | |||
193 | static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) | ||
194 | { | ||
195 | struct ccb *driver_ccb; | ||
196 | struct ccb __iomem *device_ccb; | ||
197 | int retries; | ||
198 | |||
199 | driver_ccb = &data->driver_ccb; | ||
200 | device_ccb = data->mapped_ccb; | ||
201 | |||
202 | /* complicated dance to tell the hw we are stopping */ | ||
203 | doorbell_clr(driver_ccb); | ||
204 | iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), | ||
205 | &device_ccb->send_ctrl); | ||
206 | iowrite32(ioread32(&device_ccb->recv_ctrl) & ~(1 << CTRL_BITPOS_G), | ||
207 | &device_ccb->recv_ctrl); | ||
208 | |||
209 | /* give iLO some time to process stop request */ | ||
210 | for (retries = 1000; retries > 0; retries--) { | ||
211 | doorbell_set(driver_ccb); | ||
212 | udelay(1); | ||
213 | if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A)) | ||
214 | && | ||
215 | !(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A))) | ||
216 | break; | ||
217 | } | ||
218 | if (retries == 0) | ||
219 | dev_err(&pdev->dev, "Closing, but controller still active\n"); | ||
220 | |||
221 | /* clear the hw ccb */ | ||
222 | memset_io(device_ccb, 0, sizeof(struct ccb)); | ||
223 | |||
224 | /* free resources used to back send/recv queues */ | ||
225 | pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); | ||
226 | } | ||
227 | |||
228 | static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) | ||
229 | { | ||
230 | char *dma_va, *dma_pa; | ||
231 | int pkt_id, pkt_sz, i, error; | ||
232 | struct ccb *driver_ccb, *ilo_ccb; | ||
233 | struct pci_dev *pdev; | ||
234 | |||
235 | driver_ccb = &data->driver_ccb; | ||
236 | ilo_ccb = &data->ilo_ccb; | ||
237 | pdev = hw->ilo_dev; | ||
238 | |||
239 | data->dma_size = 2 * fifo_sz(NR_QENTRY) + | ||
240 | 2 * desc_mem_sz(NR_QENTRY) + | ||
241 | ILO_START_ALIGN + ILO_CACHE_SZ; | ||
242 | |||
243 | error = -ENOMEM; | ||
244 | data->dma_va = pci_alloc_consistent(pdev, data->dma_size, | ||
245 | &data->dma_pa); | ||
246 | if (!data->dma_va) | ||
247 | goto out; | ||
248 | |||
249 | dma_va = (char *)data->dma_va; | ||
250 | dma_pa = (char *)data->dma_pa; | ||
251 | |||
252 | memset(dma_va, 0, data->dma_size); | ||
253 | |||
254 | dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN); | ||
255 | dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_START_ALIGN); | ||
256 | |||
257 | /* | ||
258 | * Create two ccb's, one with virt addrs, one with phys addrs. | ||
259 | * Copy the phys addr ccb to device shared mem. | ||
260 | */ | ||
261 | ctrl_setup(driver_ccb, NR_QENTRY, L2_QENTRY_SZ); | ||
262 | ctrl_setup(ilo_ccb, NR_QENTRY, L2_QENTRY_SZ); | ||
263 | |||
264 | fifo_setup(dma_va, NR_QENTRY); | ||
265 | driver_ccb->ccb_u1.send_fifobar = dma_va + FIFOHANDLESIZE; | ||
266 | ilo_ccb->ccb_u1.send_fifobar = dma_pa + FIFOHANDLESIZE; | ||
267 | dma_va += fifo_sz(NR_QENTRY); | ||
268 | dma_pa += fifo_sz(NR_QENTRY); | ||
269 | |||
270 | dma_va = (char *)roundup((unsigned long)dma_va, ILO_CACHE_SZ); | ||
271 | dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_CACHE_SZ); | ||
272 | |||
273 | fifo_setup(dma_va, NR_QENTRY); | ||
274 | driver_ccb->ccb_u3.recv_fifobar = dma_va + FIFOHANDLESIZE; | ||
275 | ilo_ccb->ccb_u3.recv_fifobar = dma_pa + FIFOHANDLESIZE; | ||
276 | dma_va += fifo_sz(NR_QENTRY); | ||
277 | dma_pa += fifo_sz(NR_QENTRY); | ||
278 | |||
279 | driver_ccb->ccb_u2.send_desc = dma_va; | ||
280 | ilo_ccb->ccb_u2.send_desc = dma_pa; | ||
281 | dma_pa += desc_mem_sz(NR_QENTRY); | ||
282 | dma_va += desc_mem_sz(NR_QENTRY); | ||
283 | |||
284 | driver_ccb->ccb_u4.recv_desc = dma_va; | ||
285 | ilo_ccb->ccb_u4.recv_desc = dma_pa; | ||
286 | |||
287 | driver_ccb->channel = slot; | ||
288 | ilo_ccb->channel = slot; | ||
289 | |||
290 | driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); | ||
291 | ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ | ||
292 | |||
293 | /* copy the ccb with physical addrs to device memory */ | ||
294 | data->mapped_ccb = (struct ccb __iomem *) | ||
295 | (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); | ||
296 | memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb)); | ||
297 | |||
298 | /* put packets on the send and receive queues */ | ||
299 | pkt_sz = 0; | ||
300 | for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) { | ||
301 | ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, pkt_sz); | ||
302 | doorbell_set(driver_ccb); | ||
303 | } | ||
304 | |||
305 | pkt_sz = desc_mem_sz(1); | ||
306 | for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) | ||
307 | ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); | ||
308 | |||
309 | doorbell_clr(driver_ccb); | ||
310 | |||
311 | /* make sure iLO is really handling requests */ | ||
312 | for (i = 1000; i > 0; i--) { | ||
313 | if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL)) | ||
314 | break; | ||
315 | udelay(1); | ||
316 | } | ||
317 | |||
318 | if (i) { | ||
319 | ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); | ||
320 | doorbell_set(driver_ccb); | ||
321 | } else { | ||
322 | dev_err(&pdev->dev, "Open could not dequeue a packet\n"); | ||
323 | error = -EBUSY; | ||
324 | goto free; | ||
325 | } | ||
326 | |||
327 | return 0; | ||
328 | free: | ||
329 | pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); | ||
330 | out: | ||
331 | return error; | ||
332 | } | ||
333 | |||
334 | static inline int is_channel_reset(struct ccb *ccb) | ||
335 | { | ||
336 | /* check for this particular channel needing a reset */ | ||
337 | return FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset; | ||
338 | } | ||
339 | |||
340 | static inline void set_channel_reset(struct ccb *ccb) | ||
341 | { | ||
342 | /* set a flag indicating this channel needs a reset */ | ||
343 | FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; | ||
344 | } | ||
345 | |||
346 | static inline int is_device_reset(struct ilo_hwinfo *hw) | ||
347 | { | ||
348 | /* check for global reset condition */ | ||
349 | return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET); | ||
350 | } | ||
351 | |||
352 | static inline void clear_device(struct ilo_hwinfo *hw) | ||
353 | { | ||
354 | /* clear the device (reset bits, pending channel entries) */ | ||
355 | iowrite32(-1, &hw->mmio_vaddr[DB_OUT]); | ||
356 | } | ||
357 | |||
358 | static void ilo_locked_reset(struct ilo_hwinfo *hw) | ||
359 | { | ||
360 | int slot; | ||
361 | |||
362 | /* | ||
363 | * Mapped memory is zeroed on ilo reset, so set a per ccb flag | ||
364 | * to indicate that this ccb needs to be closed and reopened. | ||
365 | */ | ||
366 | for (slot = 0; slot < MAX_CCB; slot++) { | ||
367 | if (!hw->ccb_alloc[slot]) | ||
368 | continue; | ||
369 | set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); | ||
370 | } | ||
371 | |||
372 | clear_device(hw); | ||
373 | } | ||
374 | |||
375 | static void ilo_reset(struct ilo_hwinfo *hw) | ||
376 | { | ||
377 | spin_lock(&hw->alloc_lock); | ||
378 | |||
379 | /* reset might have been handled after lock was taken */ | ||
380 | if (is_device_reset(hw)) | ||
381 | ilo_locked_reset(hw); | ||
382 | |||
383 | spin_unlock(&hw->alloc_lock); | ||
384 | } | ||
385 | |||
386 | static ssize_t ilo_read(struct file *fp, char __user *buf, | ||
387 | size_t len, loff_t *off) | ||
388 | { | ||
389 | int err, found, cnt, pkt_id, pkt_len; | ||
390 | struct ccb_data *data; | ||
391 | struct ccb *driver_ccb; | ||
392 | struct ilo_hwinfo *hw; | ||
393 | void *pkt; | ||
394 | |||
395 | data = fp->private_data; | ||
396 | driver_ccb = &data->driver_ccb; | ||
397 | hw = data->ilo_hw; | ||
398 | |||
399 | if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { | ||
400 | /* | ||
401 | * If the device has been reset, applications | ||
402 | * need to close and reopen all ccbs. | ||
403 | */ | ||
404 | ilo_reset(hw); | ||
405 | return -ENODEV; | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * This function is to be called when data is expected | ||
410 | * in the channel, and will return an error if no packet is found | ||
411 | * during the loop below. The sleep/retry logic is to allow | ||
412 | * applications to call read() immediately post write(), | ||
413 | * and give iLO some time to process the sent packet. | ||
414 | */ | ||
415 | cnt = 20; | ||
416 | do { | ||
417 | /* look for a received packet */ | ||
418 | found = ilo_pkt_dequeue(hw, driver_ccb, RECVQ, &pkt_id, | ||
419 | &pkt_len, &pkt); | ||
420 | if (found) | ||
421 | break; | ||
422 | cnt--; | ||
423 | msleep(100); | ||
424 | } while (!found && cnt); | ||
425 | |||
426 | if (!found) | ||
427 | return -EAGAIN; | ||
428 | |||
429 | /* only copy the length of the received packet */ | ||
430 | if (pkt_len < len) | ||
431 | len = pkt_len; | ||
432 | |||
433 | err = copy_to_user(buf, pkt, len); | ||
434 | |||
435 | /* return the received packet to the queue */ | ||
436 | ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, desc_mem_sz(1)); | ||
437 | |||
438 | return err ? -EFAULT : len; | ||
439 | } | ||
440 | |||
441 | static ssize_t ilo_write(struct file *fp, const char __user *buf, | ||
442 | size_t len, loff_t *off) | ||
443 | { | ||
444 | int err, pkt_id, pkt_len; | ||
445 | struct ccb_data *data; | ||
446 | struct ccb *driver_ccb; | ||
447 | struct ilo_hwinfo *hw; | ||
448 | void *pkt; | ||
449 | |||
450 | data = fp->private_data; | ||
451 | driver_ccb = &data->driver_ccb; | ||
452 | hw = data->ilo_hw; | ||
453 | |||
454 | if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { | ||
455 | /* | ||
456 | * If the device has been reset, applications | ||
457 | * need to close and reopen all ccbs. | ||
458 | */ | ||
459 | ilo_reset(hw); | ||
460 | return -ENODEV; | ||
461 | } | ||
462 | |||
463 | /* get a packet to send the user command */ | ||
464 | if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) | ||
465 | return -EBUSY; | ||
466 | |||
467 | /* limit the length to the length of the packet */ | ||
468 | if (pkt_len < len) | ||
469 | len = pkt_len; | ||
470 | |||
471 | /* on failure, set the len to 0 to return empty packet to the device */ | ||
472 | err = copy_from_user(pkt, buf, len); | ||
473 | if (err) | ||
474 | len = 0; | ||
475 | |||
476 | /* send the packet */ | ||
477 | ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, len); | ||
478 | doorbell_set(driver_ccb); | ||
479 | |||
480 | return err ? -EFAULT : len; | ||
481 | } | ||
482 | |||
483 | static int ilo_close(struct inode *ip, struct file *fp) | ||
484 | { | ||
485 | int slot; | ||
486 | struct ccb_data *data; | ||
487 | struct ilo_hwinfo *hw; | ||
488 | |||
489 | slot = iminor(ip) % MAX_CCB; | ||
490 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); | ||
491 | |||
492 | spin_lock(&hw->alloc_lock); | ||
493 | |||
494 | if (is_device_reset(hw)) | ||
495 | ilo_locked_reset(hw); | ||
496 | |||
497 | if (hw->ccb_alloc[slot]->ccb_cnt == 1) { | ||
498 | |||
499 | data = fp->private_data; | ||
500 | |||
501 | ilo_ccb_close(hw->ilo_dev, data); | ||
502 | |||
503 | kfree(data); | ||
504 | hw->ccb_alloc[slot] = NULL; | ||
505 | } else | ||
506 | hw->ccb_alloc[slot]->ccb_cnt--; | ||
507 | |||
508 | spin_unlock(&hw->alloc_lock); | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static int ilo_open(struct inode *ip, struct file *fp) | ||
514 | { | ||
515 | int slot, error; | ||
516 | struct ccb_data *data; | ||
517 | struct ilo_hwinfo *hw; | ||
518 | |||
519 | slot = iminor(ip) % MAX_CCB; | ||
520 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); | ||
521 | |||
522 | /* new ccb allocation */ | ||
523 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
524 | if (!data) | ||
525 | return -ENOMEM; | ||
526 | |||
527 | spin_lock(&hw->alloc_lock); | ||
528 | |||
529 | if (is_device_reset(hw)) | ||
530 | ilo_locked_reset(hw); | ||
531 | |||
532 | /* each fd private_data holds sw/hw view of ccb */ | ||
533 | if (hw->ccb_alloc[slot] == NULL) { | ||
534 | /* create a channel control block for this minor */ | ||
535 | error = ilo_ccb_open(hw, data, slot); | ||
536 | if (!error) { | ||
537 | hw->ccb_alloc[slot] = data; | ||
538 | hw->ccb_alloc[slot]->ccb_cnt = 1; | ||
539 | hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL; | ||
540 | hw->ccb_alloc[slot]->ilo_hw = hw; | ||
541 | } else | ||
542 | kfree(data); | ||
543 | } else { | ||
544 | kfree(data); | ||
545 | if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { | ||
546 | /* | ||
547 | * The channel exists, and either this open | ||
548 | * or a previous open of this channel wants | ||
549 | * exclusive access. | ||
550 | */ | ||
551 | error = -EBUSY; | ||
552 | } else { | ||
553 | hw->ccb_alloc[slot]->ccb_cnt++; | ||
554 | error = 0; | ||
555 | } | ||
556 | } | ||
557 | spin_unlock(&hw->alloc_lock); | ||
558 | |||
559 | if (!error) | ||
560 | fp->private_data = hw->ccb_alloc[slot]; | ||
561 | |||
562 | return error; | ||
563 | } | ||
564 | |||
565 | static const struct file_operations ilo_fops = { | ||
566 | .owner = THIS_MODULE, | ||
567 | .read = ilo_read, | ||
568 | .write = ilo_write, | ||
569 | .open = ilo_open, | ||
570 | .release = ilo_close, | ||
571 | }; | ||
572 | |||
573 | static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) | ||
574 | { | ||
575 | pci_iounmap(pdev, hw->db_vaddr); | ||
576 | pci_iounmap(pdev, hw->ram_vaddr); | ||
577 | pci_iounmap(pdev, hw->mmio_vaddr); | ||
578 | } | ||
579 | |||
580 | static int __devinit ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) | ||
581 | { | ||
582 | int error = -ENOMEM; | ||
583 | |||
584 | /* map the memory mapped i/o registers */ | ||
585 | hw->mmio_vaddr = pci_iomap(pdev, 1, 0); | ||
586 | if (hw->mmio_vaddr == NULL) { | ||
587 | dev_err(&pdev->dev, "Error mapping mmio\n"); | ||
588 | goto out; | ||
589 | } | ||
590 | |||
591 | /* map the adapter shared memory region */ | ||
592 | hw->ram_vaddr = pci_iomap(pdev, 2, MAX_CCB * ILOHW_CCB_SZ); | ||
593 | if (hw->ram_vaddr == NULL) { | ||
594 | dev_err(&pdev->dev, "Error mapping shared mem\n"); | ||
595 | goto mmio_free; | ||
596 | } | ||
597 | |||
598 | /* map the doorbell aperture */ | ||
599 | hw->db_vaddr = pci_iomap(pdev, 3, MAX_CCB * ONE_DB_SIZE); | ||
600 | if (hw->db_vaddr == NULL) { | ||
601 | dev_err(&pdev->dev, "Error mapping doorbell\n"); | ||
602 | goto ram_free; | ||
603 | } | ||
604 | |||
605 | return 0; | ||
606 | ram_free: | ||
607 | pci_iounmap(pdev, hw->ram_vaddr); | ||
608 | mmio_free: | ||
609 | pci_iounmap(pdev, hw->mmio_vaddr); | ||
610 | out: | ||
611 | return error; | ||
612 | } | ||
613 | |||
614 | static void ilo_remove(struct pci_dev *pdev) | ||
615 | { | ||
616 | int i, minor; | ||
617 | struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev); | ||
618 | |||
619 | clear_device(ilo_hw); | ||
620 | |||
621 | minor = MINOR(ilo_hw->cdev.dev); | ||
622 | for (i = minor; i < minor + MAX_CCB; i++) | ||
623 | device_destroy(ilo_class, MKDEV(ilo_major, i)); | ||
624 | |||
625 | cdev_del(&ilo_hw->cdev); | ||
626 | ilo_unmap_device(pdev, ilo_hw); | ||
627 | pci_release_regions(pdev); | ||
628 | pci_disable_device(pdev); | ||
629 | kfree(ilo_hw); | ||
630 | ilo_hwdev[(minor / MAX_CCB)] = 0; | ||
631 | } | ||
632 | |||
633 | static int __devinit ilo_probe(struct pci_dev *pdev, | ||
634 | const struct pci_device_id *ent) | ||
635 | { | ||
636 | int devnum, minor, start, error; | ||
637 | struct ilo_hwinfo *ilo_hw; | ||
638 | |||
639 | /* find a free range for device files */ | ||
640 | for (devnum = 0; devnum < MAX_ILO_DEV; devnum++) { | ||
641 | if (ilo_hwdev[devnum] == 0) { | ||
642 | ilo_hwdev[devnum] = 1; | ||
643 | break; | ||
644 | } | ||
645 | } | ||
646 | |||
647 | if (devnum == MAX_ILO_DEV) { | ||
648 | dev_err(&pdev->dev, "Error finding free device\n"); | ||
649 | return -ENODEV; | ||
650 | } | ||
651 | |||
652 | /* track global allocations for this device */ | ||
653 | error = -ENOMEM; | ||
654 | ilo_hw = kzalloc(sizeof(*ilo_hw), GFP_KERNEL); | ||
655 | if (!ilo_hw) | ||
656 | goto out; | ||
657 | |||
658 | ilo_hw->ilo_dev = pdev; | ||
659 | spin_lock_init(&ilo_hw->alloc_lock); | ||
660 | spin_lock_init(&ilo_hw->fifo_lock); | ||
661 | |||
662 | error = pci_enable_device(pdev); | ||
663 | if (error) | ||
664 | goto free; | ||
665 | |||
666 | pci_set_master(pdev); | ||
667 | |||
668 | error = pci_request_regions(pdev, ILO_NAME); | ||
669 | if (error) | ||
670 | goto disable; | ||
671 | |||
672 | error = ilo_map_device(pdev, ilo_hw); | ||
673 | if (error) | ||
674 | goto free_regions; | ||
675 | |||
676 | pci_set_drvdata(pdev, ilo_hw); | ||
677 | clear_device(ilo_hw); | ||
678 | |||
679 | cdev_init(&ilo_hw->cdev, &ilo_fops); | ||
680 | ilo_hw->cdev.owner = THIS_MODULE; | ||
681 | start = devnum * MAX_CCB; | ||
682 | error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); | ||
683 | if (error) { | ||
684 | dev_err(&pdev->dev, "Could not add cdev\n"); | ||
685 | goto unmap; | ||
686 | } | ||
687 | |||
688 | for (minor = 0 ; minor < MAX_CCB; minor++) { | ||
689 | struct device *dev; | ||
690 | dev = device_create(ilo_class, &pdev->dev, | ||
691 | MKDEV(ilo_major, minor), NULL, | ||
692 | "hpilo!d%dccb%d", devnum, minor); | ||
693 | if (IS_ERR(dev)) | ||
694 | dev_err(&pdev->dev, "Could not create files\n"); | ||
695 | } | ||
696 | |||
697 | return 0; | ||
698 | unmap: | ||
699 | ilo_unmap_device(pdev, ilo_hw); | ||
700 | free_regions: | ||
701 | pci_release_regions(pdev); | ||
702 | disable: | ||
703 | pci_disable_device(pdev); | ||
704 | free: | ||
705 | kfree(ilo_hw); | ||
706 | out: | ||
707 | ilo_hwdev[devnum] = 0; | ||
708 | return error; | ||
709 | } | ||
710 | |||
711 | static struct pci_device_id ilo_devices[] = { | ||
712 | { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) }, | ||
713 | { } | ||
714 | }; | ||
715 | MODULE_DEVICE_TABLE(pci, ilo_devices); | ||
716 | |||
717 | static struct pci_driver ilo_driver = { | ||
718 | .name = ILO_NAME, | ||
719 | .id_table = ilo_devices, | ||
720 | .probe = ilo_probe, | ||
721 | .remove = __devexit_p(ilo_remove), | ||
722 | }; | ||
723 | |||
724 | static int __init ilo_init(void) | ||
725 | { | ||
726 | int error; | ||
727 | dev_t dev; | ||
728 | |||
729 | ilo_class = class_create(THIS_MODULE, "iLO"); | ||
730 | if (IS_ERR(ilo_class)) { | ||
731 | error = PTR_ERR(ilo_class); | ||
732 | goto out; | ||
733 | } | ||
734 | |||
735 | error = alloc_chrdev_region(&dev, 0, MAX_OPEN, ILO_NAME); | ||
736 | if (error) | ||
737 | goto class_destroy; | ||
738 | |||
739 | ilo_major = MAJOR(dev); | ||
740 | |||
741 | error = pci_register_driver(&ilo_driver); | ||
742 | if (error) | ||
743 | goto chr_remove; | ||
744 | |||
745 | return 0; | ||
746 | chr_remove: | ||
747 | unregister_chrdev_region(dev, MAX_OPEN); | ||
748 | class_destroy: | ||
749 | class_destroy(ilo_class); | ||
750 | out: | ||
751 | return error; | ||
752 | } | ||
753 | |||
754 | static void __exit ilo_exit(void) | ||
755 | { | ||
756 | pci_unregister_driver(&ilo_driver); | ||
757 | unregister_chrdev_region(MKDEV(ilo_major, 0), MAX_OPEN); | ||
758 | class_destroy(ilo_class); | ||
759 | } | ||
760 | |||
761 | MODULE_VERSION("0.05"); | ||
762 | MODULE_ALIAS(ILO_NAME); | ||
763 | MODULE_DESCRIPTION(ILO_NAME); | ||
764 | MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>"); | ||
765 | MODULE_LICENSE("GPL v2"); | ||
766 | |||
767 | module_init(ilo_init); | ||
768 | module_exit(ilo_exit); | ||
diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h new file mode 100644 index 000000000000..a281207696c1 --- /dev/null +++ b/drivers/misc/hpilo.h | |||
@@ -0,0 +1,189 @@ | |||
1 | /* | ||
2 | * linux/drivers/char/hpilo.h | ||
3 | * | ||
4 | * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. | ||
5 | * David Altobelli <david.altobelli@hp.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef __HPILO_H | ||
12 | #define __HPILO_H | ||
13 | |||
14 | #define ILO_NAME "hpilo" | ||
15 | |||
16 | /* max number of open channel control blocks per device, hw limited to 32 */ | ||
17 | #define MAX_CCB 8 | ||
18 | /* max number of supported devices */ | ||
19 | #define MAX_ILO_DEV 1 | ||
20 | /* max number of files */ | ||
21 | #define MAX_OPEN (MAX_CCB * MAX_ILO_DEV) | ||
22 | |||
23 | /* | ||
24 | * Per device, used to track global memory allocations. | ||
25 | */ | ||
26 | struct ilo_hwinfo { | ||
27 | /* mmio registers on device */ | ||
28 | char __iomem *mmio_vaddr; | ||
29 | |||
30 | /* doorbell registers on device */ | ||
31 | char __iomem *db_vaddr; | ||
32 | |||
33 | /* shared memory on device used for channel control blocks */ | ||
34 | char __iomem *ram_vaddr; | ||
35 | |||
36 | /* files corresponding to this device */ | ||
37 | struct ccb_data *ccb_alloc[MAX_CCB]; | ||
38 | |||
39 | struct pci_dev *ilo_dev; | ||
40 | |||
41 | spinlock_t alloc_lock; | ||
42 | spinlock_t fifo_lock; | ||
43 | |||
44 | struct cdev cdev; | ||
45 | }; | ||
46 | |||
47 | /* offset from mmio_vaddr */ | ||
48 | #define DB_OUT 0xD4 | ||
49 | /* DB_OUT reset bit */ | ||
50 | #define DB_RESET 26 | ||
51 | |||
52 | /* | ||
53 | * Channel control block. Used to manage hardware queues. | ||
54 | * The format must match hw's version. The hw ccb is 128 bytes, | ||
55 | * but the context area shouldn't be touched by the driver. | ||
56 | */ | ||
57 | #define ILOSW_CCB_SZ 64 | ||
58 | #define ILOHW_CCB_SZ 128 | ||
59 | struct ccb { | ||
60 | union { | ||
61 | char *send_fifobar; | ||
62 | u64 padding1; | ||
63 | } ccb_u1; | ||
64 | union { | ||
65 | char *send_desc; | ||
66 | u64 padding2; | ||
67 | } ccb_u2; | ||
68 | u64 send_ctrl; | ||
69 | |||
70 | union { | ||
71 | char *recv_fifobar; | ||
72 | u64 padding3; | ||
73 | } ccb_u3; | ||
74 | union { | ||
75 | char *recv_desc; | ||
76 | u64 padding4; | ||
77 | } ccb_u4; | ||
78 | u64 recv_ctrl; | ||
79 | |||
80 | union { | ||
81 | char __iomem *db_base; | ||
82 | u64 padding5; | ||
83 | } ccb_u5; | ||
84 | |||
85 | u64 channel; | ||
86 | |||
87 | /* unused context area (64 bytes) */ | ||
88 | }; | ||
89 | |||
90 | /* ccb queue parameters */ | ||
91 | #define SENDQ 1 | ||
92 | #define RECVQ 2 | ||
93 | #define NR_QENTRY 4 | ||
94 | #define L2_QENTRY_SZ 12 | ||
95 | |||
96 | /* ccb ctrl bitfields */ | ||
97 | #define CTRL_BITPOS_L2SZ 0 | ||
98 | #define CTRL_BITPOS_FIFOINDEXMASK 4 | ||
99 | #define CTRL_BITPOS_DESCLIMIT 18 | ||
100 | #define CTRL_BITPOS_A 30 | ||
101 | #define CTRL_BITPOS_G 31 | ||
102 | |||
103 | /* ccb doorbell macros */ | ||
104 | #define L2_DB_SIZE 14 | ||
105 | #define ONE_DB_SIZE (1 << L2_DB_SIZE) | ||
106 | |||
107 | /* | ||
108 | * Per fd structure used to track the ccb allocated to that dev file. | ||
109 | */ | ||
110 | struct ccb_data { | ||
111 | /* software version of ccb, using virtual addrs */ | ||
112 | struct ccb driver_ccb; | ||
113 | |||
114 | /* hardware version of ccb, using physical addrs */ | ||
115 | struct ccb ilo_ccb; | ||
116 | |||
117 | /* hardware ccb is written to this shared mapped device memory */ | ||
118 | struct ccb __iomem *mapped_ccb; | ||
119 | |||
120 | /* dma'able memory used for send/recv queues */ | ||
121 | void *dma_va; | ||
122 | dma_addr_t dma_pa; | ||
123 | size_t dma_size; | ||
124 | |||
125 | /* pointer to hardware device info */ | ||
126 | struct ilo_hwinfo *ilo_hw; | ||
127 | |||
128 | /* usage count, to allow for shared ccb's */ | ||
129 | int ccb_cnt; | ||
130 | |||
131 | /* open wanted exclusive access to this ccb */ | ||
132 | int ccb_excl; | ||
133 | }; | ||
134 | |||
135 | /* | ||
136 | * FIFO queue structure, shared with hw. | ||
137 | */ | ||
138 | #define ILO_START_ALIGN 4096 | ||
139 | #define ILO_CACHE_SZ 128 | ||
140 | struct fifo { | ||
141 | u64 nrents; /* user requested number of fifo entries */ | ||
142 | u64 imask; /* mask to extract valid fifo index */ | ||
143 | u64 merge; /* O/C bits to merge in during enqueue operation */ | ||
144 | u64 reset; /* set to non-zero when the target device resets */ | ||
145 | u8 pad_0[ILO_CACHE_SZ - (sizeof(u64) * 4)]; | ||
146 | |||
147 | u64 head; | ||
148 | u8 pad_1[ILO_CACHE_SZ - (sizeof(u64))]; | ||
149 | |||
150 | u64 tail; | ||
151 | u8 pad_2[ILO_CACHE_SZ - (sizeof(u64))]; | ||
152 | |||
153 | u64 fifobar[1]; | ||
154 | }; | ||
155 | |||
156 | /* convert between struct fifo, and the fifobar, which is saved in the ccb */ | ||
157 | #define FIFOHANDLESIZE (sizeof(struct fifo) - sizeof(u64)) | ||
158 | #define FIFOBARTOHANDLE(_fifo) \ | ||
159 | ((struct fifo *)(((char *)(_fifo)) - FIFOHANDLESIZE)) | ||
160 | |||
161 | /* the number of qwords to consume from the entry descriptor */ | ||
162 | #define ENTRY_BITPOS_QWORDS 0 | ||
163 | /* descriptor index number (within a specified queue) */ | ||
164 | #define ENTRY_BITPOS_DESCRIPTOR 10 | ||
165 | /* state bit, fifo entry consumed by consumer */ | ||
166 | #define ENTRY_BITPOS_C 22 | ||
167 | /* state bit, fifo entry is occupied */ | ||
168 | #define ENTRY_BITPOS_O 23 | ||
169 | |||
170 | #define ENTRY_BITS_QWORDS 10 | ||
171 | #define ENTRY_BITS_DESCRIPTOR 12 | ||
172 | #define ENTRY_BITS_C 1 | ||
173 | #define ENTRY_BITS_O 1 | ||
174 | #define ENTRY_BITS_TOTAL \ | ||
175 | (ENTRY_BITS_C + ENTRY_BITS_O + \ | ||
176 | ENTRY_BITS_QWORDS + ENTRY_BITS_DESCRIPTOR) | ||
177 | |||
178 | /* extract various entry fields */ | ||
179 | #define ENTRY_MASK ((1 << ENTRY_BITS_TOTAL) - 1) | ||
180 | #define ENTRY_MASK_C (((1 << ENTRY_BITS_C) - 1) << ENTRY_BITPOS_C) | ||
181 | #define ENTRY_MASK_O (((1 << ENTRY_BITS_O) - 1) << ENTRY_BITPOS_O) | ||
182 | #define ENTRY_MASK_QWORDS \ | ||
183 | (((1 << ENTRY_BITS_QWORDS) - 1) << ENTRY_BITPOS_QWORDS) | ||
184 | #define ENTRY_MASK_DESCRIPTOR \ | ||
185 | (((1 << ENTRY_BITS_DESCRIPTOR) - 1) << ENTRY_BITPOS_DESCRIPTOR) | ||
186 | |||
187 | #define ENTRY_MASK_NOSTATE (ENTRY_MASK >> (ENTRY_BITS_C + ENTRY_BITS_O)) | ||
188 | |||
189 | #endif /* __HPILO_H */ | ||
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index fa394104339c..e4ff50b95a5e 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c | |||
@@ -102,7 +102,6 @@ | |||
102 | #include <linux/nmi.h> | 102 | #include <linux/nmi.h> |
103 | #include <linux/delay.h> | 103 | #include <linux/delay.h> |
104 | #include <linux/kthread.h> | 104 | #include <linux/kthread.h> |
105 | #include <linux/delay.h> | ||
106 | 105 | ||
107 | #define v1printk(a...) do { \ | 106 | #define v1printk(a...) do { \ |
108 | if (verbose) \ | 107 | if (verbose) \ |
@@ -119,7 +118,6 @@ | |||
119 | } while (0) | 118 | } while (0) |
120 | #define MAX_CONFIG_LEN 40 | 119 | #define MAX_CONFIG_LEN 40 |
121 | 120 | ||
122 | static const char hexchars[] = "0123456789abcdef"; | ||
123 | static struct kgdb_io kgdbts_io_ops; | 121 | static struct kgdb_io kgdbts_io_ops; |
124 | static char get_buf[BUFMAX]; | 122 | static char get_buf[BUFMAX]; |
125 | static int get_buf_cnt; | 123 | static int get_buf_cnt; |
@@ -131,6 +129,8 @@ static int repeat_test; | |||
131 | static int test_complete; | 129 | static int test_complete; |
132 | static int send_ack; | 130 | static int send_ack; |
133 | static int final_ack; | 131 | static int final_ack; |
132 | static int force_hwbrks; | ||
133 | static int hwbreaks_ok; | ||
134 | static int hw_break_val; | 134 | static int hw_break_val; |
135 | static int hw_break_val2; | 135 | static int hw_break_val2; |
136 | #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) | 136 | #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) |
@@ -234,12 +234,12 @@ static void break_helper(char *bp_type, char *arg, unsigned long vaddr) | |||
234 | 234 | ||
235 | static void sw_break(char *arg) | 235 | static void sw_break(char *arg) |
236 | { | 236 | { |
237 | break_helper("Z0", arg, 0); | 237 | break_helper(force_hwbrks ? "Z1" : "Z0", arg, 0); |
238 | } | 238 | } |
239 | 239 | ||
240 | static void sw_rem_break(char *arg) | 240 | static void sw_rem_break(char *arg) |
241 | { | 241 | { |
242 | break_helper("z0", arg, 0); | 242 | break_helper(force_hwbrks ? "z1" : "z0", arg, 0); |
243 | } | 243 | } |
244 | 244 | ||
245 | static void hw_break(char *arg) | 245 | static void hw_break(char *arg) |
@@ -619,8 +619,8 @@ static void fill_get_buf(char *buf) | |||
619 | count++; | 619 | count++; |
620 | } | 620 | } |
621 | strcat(get_buf, "#"); | 621 | strcat(get_buf, "#"); |
622 | get_buf[count + 2] = hexchars[checksum >> 4]; | 622 | get_buf[count + 2] = hex_asc_hi(checksum); |
623 | get_buf[count + 3] = hexchars[checksum & 0xf]; | 623 | get_buf[count + 3] = hex_asc_lo(checksum); |
624 | get_buf[count + 4] = '\0'; | 624 | get_buf[count + 4] = '\0'; |
625 | v2printk("get%i: %s\n", ts.idx, get_buf); | 625 | v2printk("get%i: %s\n", ts.idx, get_buf); |
626 | } | 626 | } |
@@ -781,6 +781,8 @@ static void run_breakpoint_test(int is_hw_breakpoint) | |||
781 | return; | 781 | return; |
782 | 782 | ||
783 | eprintk("kgdbts: ERROR %s test failed\n", ts.name); | 783 | eprintk("kgdbts: ERROR %s test failed\n", ts.name); |
784 | if (is_hw_breakpoint) | ||
785 | hwbreaks_ok = 0; | ||
784 | } | 786 | } |
785 | 787 | ||
786 | static void run_hw_break_test(int is_write_test) | 788 | static void run_hw_break_test(int is_write_test) |
@@ -798,9 +800,11 @@ static void run_hw_break_test(int is_write_test) | |||
798 | kgdb_breakpoint(); | 800 | kgdb_breakpoint(); |
799 | hw_break_val_access(); | 801 | hw_break_val_access(); |
800 | if (is_write_test) { | 802 | if (is_write_test) { |
801 | if (test_complete == 2) | 803 | if (test_complete == 2) { |
802 | eprintk("kgdbts: ERROR %s broke on access\n", | 804 | eprintk("kgdbts: ERROR %s broke on access\n", |
803 | ts.name); | 805 | ts.name); |
806 | hwbreaks_ok = 0; | ||
807 | } | ||
804 | hw_break_val_write(); | 808 | hw_break_val_write(); |
805 | } | 809 | } |
806 | kgdb_breakpoint(); | 810 | kgdb_breakpoint(); |
@@ -809,6 +813,7 @@ static void run_hw_break_test(int is_write_test) | |||
809 | return; | 813 | return; |
810 | 814 | ||
811 | eprintk("kgdbts: ERROR %s test failed\n", ts.name); | 815 | eprintk("kgdbts: ERROR %s test failed\n", ts.name); |
816 | hwbreaks_ok = 0; | ||
812 | } | 817 | } |
813 | 818 | ||
814 | static void run_nmi_sleep_test(int nmi_sleep) | 819 | static void run_nmi_sleep_test(int nmi_sleep) |
@@ -912,6 +917,7 @@ static void kgdbts_run_tests(void) | |||
912 | 917 | ||
913 | /* All HW break point tests */ | 918 | /* All HW break point tests */ |
914 | if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) { | 919 | if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) { |
920 | hwbreaks_ok = 1; | ||
915 | v1printk("kgdbts:RUN hw breakpoint test\n"); | 921 | v1printk("kgdbts:RUN hw breakpoint test\n"); |
916 | run_breakpoint_test(1); | 922 | run_breakpoint_test(1); |
917 | v1printk("kgdbts:RUN hw write breakpoint test\n"); | 923 | v1printk("kgdbts:RUN hw write breakpoint test\n"); |
@@ -925,6 +931,19 @@ static void kgdbts_run_tests(void) | |||
925 | run_nmi_sleep_test(nmi_sleep); | 931 | run_nmi_sleep_test(nmi_sleep); |
926 | } | 932 | } |
927 | 933 | ||
934 | #ifdef CONFIG_DEBUG_RODATA | ||
935 | /* Until there is an api to write to read-only text segments, use | ||
936 | * HW breakpoints for the remainder of any tests, else print a | ||
937 | * failure message if hw breakpoints do not work. | ||
938 | */ | ||
939 | if (!(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT && hwbreaks_ok)) { | ||
940 | eprintk("kgdbts: HW breakpoints do not work," | ||
941 | "skipping remaining tests\n"); | ||
942 | return; | ||
943 | } | ||
944 | force_hwbrks = 1; | ||
945 | #endif /* CONFIG_DEBUG_RODATA */ | ||
946 | |||
928 | /* If the do_fork test is run it will be the last test that is | 947 | /* If the do_fork test is run it will be the last test that is |
929 | * executed because a kernel thread will be spawned at the very | 948 | * executed because a kernel thread will be spawned at the very |
930 | * end to unregister the debug hooks. | 949 | * end to unregister the debug hooks. |
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 71d1c84e2fa8..daf585689ce3 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
23 | #include <linux/cdev.h> | 23 | #include <linux/cdev.h> |
24 | #include <linux/phantom.h> | 24 | #include <linux/phantom.h> |
25 | #include <linux/smp_lock.h> | ||
25 | 26 | ||
26 | #include <asm/atomic.h> | 27 | #include <asm/atomic.h> |
27 | #include <asm/io.h> | 28 | #include <asm/io.h> |
@@ -212,13 +213,17 @@ static int phantom_open(struct inode *inode, struct file *file) | |||
212 | struct phantom_device *dev = container_of(inode->i_cdev, | 213 | struct phantom_device *dev = container_of(inode->i_cdev, |
213 | struct phantom_device, cdev); | 214 | struct phantom_device, cdev); |
214 | 215 | ||
216 | lock_kernel(); | ||
215 | nonseekable_open(inode, file); | 217 | nonseekable_open(inode, file); |
216 | 218 | ||
217 | if (mutex_lock_interruptible(&dev->open_lock)) | 219 | if (mutex_lock_interruptible(&dev->open_lock)) { |
220 | unlock_kernel(); | ||
218 | return -ERESTARTSYS; | 221 | return -ERESTARTSYS; |
222 | } | ||
219 | 223 | ||
220 | if (dev->opened) { | 224 | if (dev->opened) { |
221 | mutex_unlock(&dev->open_lock); | 225 | mutex_unlock(&dev->open_lock); |
226 | unlock_kernel(); | ||
222 | return -EINVAL; | 227 | return -EINVAL; |
223 | } | 228 | } |
224 | 229 | ||
@@ -229,7 +234,7 @@ static int phantom_open(struct inode *inode, struct file *file) | |||
229 | atomic_set(&dev->counter, 0); | 234 | atomic_set(&dev->counter, 0); |
230 | dev->opened++; | 235 | dev->opened++; |
231 | mutex_unlock(&dev->open_lock); | 236 | mutex_unlock(&dev->open_lock); |
232 | 237 | unlock_kernel(); | |
233 | return 0; | 238 | return 0; |
234 | } | 239 | } |
235 | 240 | ||
@@ -394,8 +399,9 @@ static int __devinit phantom_probe(struct pci_dev *pdev, | |||
394 | goto err_irq; | 399 | goto err_irq; |
395 | } | 400 | } |
396 | 401 | ||
397 | if (IS_ERR(device_create(phantom_class, &pdev->dev, MKDEV(phantom_major, | 402 | if (IS_ERR(device_create_drvdata(phantom_class, &pdev->dev, |
398 | minor), "phantom%u", minor))) | 403 | MKDEV(phantom_major, minor), |
404 | NULL, "phantom%u", minor))) | ||
399 | dev_err(&pdev->dev, "can't create device\n"); | 405 | dev_err(&pdev->dev, "can't create device\n"); |
400 | 406 | ||
401 | pci_set_drvdata(pdev, pht); | 407 | pci_set_drvdata(pdev, pht); |
@@ -557,6 +563,6 @@ module_init(phantom_init); | |||
557 | module_exit(phantom_exit); | 563 | module_exit(phantom_exit); |
558 | 564 | ||
559 | MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); | 565 | MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); |
560 | MODULE_DESCRIPTION("Sensable Phantom driver"); | 566 | MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)"); |
561 | MODULE_LICENSE("GPL"); | 567 | MODULE_LICENSE("GPL"); |
562 | MODULE_VERSION(PHANTOM_VERSION); | 568 | MODULE_VERSION(PHANTOM_VERSION); |
diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 08256ed0d9a6..579b01ff82d4 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c | |||
@@ -229,10 +229,11 @@ xpc_hb_checker(void *ignore) | |||
229 | int last_IRQ_count = 0; | 229 | int last_IRQ_count = 0; |
230 | int new_IRQ_count; | 230 | int new_IRQ_count; |
231 | int force_IRQ = 0; | 231 | int force_IRQ = 0; |
232 | cpumask_of_cpu_ptr(cpumask, XPC_HB_CHECK_CPU); | ||
232 | 233 | ||
233 | /* this thread was marked active by xpc_hb_init() */ | 234 | /* this thread was marked active by xpc_hb_init() */ |
234 | 235 | ||
235 | set_cpus_allowed(current, cpumask_of_cpu(XPC_HB_CHECK_CPU)); | 236 | set_cpus_allowed_ptr(current, cpumask); |
236 | 237 | ||
237 | /* set our heartbeating to other partitions into motion */ | 238 | /* set our heartbeating to other partitions into motion */ |
238 | xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); | 239 | xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); |
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 00e48e2a9c11..60775be22822 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/module.h> | 46 | #include <linux/module.h> |
47 | #include <linux/moduleparam.h> | 47 | #include <linux/moduleparam.h> |
48 | #include <linux/init.h> | 48 | #include <linux/init.h> |
49 | #include <linux/smp_lock.h> | ||
49 | #include <linux/types.h> | 50 | #include <linux/types.h> |
50 | #include <linux/backlight.h> | 51 | #include <linux/backlight.h> |
51 | #include <linux/platform_device.h> | 52 | #include <linux/platform_device.h> |
@@ -1927,8 +1928,10 @@ static int sonypi_misc_release(struct inode *inode, struct file *file) | |||
1927 | static int sonypi_misc_open(struct inode *inode, struct file *file) | 1928 | static int sonypi_misc_open(struct inode *inode, struct file *file) |
1928 | { | 1929 | { |
1929 | /* Flush input queue on first open */ | 1930 | /* Flush input queue on first open */ |
1931 | lock_kernel(); | ||
1930 | if (atomic_inc_return(&sonypi_compat.open_count) == 1) | 1932 | if (atomic_inc_return(&sonypi_compat.open_count) == 1) |
1931 | kfifo_reset(sonypi_compat.fifo); | 1933 | kfifo_reset(sonypi_compat.fifo); |
1934 | unlock_kernel(); | ||
1932 | return 0; | 1935 | return 0; |
1933 | } | 1936 | } |
1934 | 1937 | ||
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index a0ce0b2fa03e..d3eb7903c346 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.20" | 24 | #define TPACPI_VERSION "0.21" |
25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
26 | 26 | ||
27 | /* | 27 | /* |
@@ -68,6 +68,7 @@ | |||
68 | #include <linux/hwmon-sysfs.h> | 68 | #include <linux/hwmon-sysfs.h> |
69 | #include <linux/input.h> | 69 | #include <linux/input.h> |
70 | #include <linux/leds.h> | 70 | #include <linux/leds.h> |
71 | #include <linux/rfkill.h> | ||
71 | #include <asm/uaccess.h> | 72 | #include <asm/uaccess.h> |
72 | 73 | ||
73 | #include <linux/dmi.h> | 74 | #include <linux/dmi.h> |
@@ -144,6 +145,12 @@ enum { | |||
144 | 145 | ||
145 | #define TPACPI_MAX_ACPI_ARGS 3 | 146 | #define TPACPI_MAX_ACPI_ARGS 3 |
146 | 147 | ||
148 | /* rfkill switches */ | ||
149 | enum { | ||
150 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
151 | TPACPI_RFK_WWAN_SW_ID, | ||
152 | }; | ||
153 | |||
147 | /* Debugging */ | 154 | /* Debugging */ |
148 | #define TPACPI_LOG TPACPI_FILE ": " | 155 | #define TPACPI_LOG TPACPI_FILE ": " |
149 | #define TPACPI_ERR KERN_ERR TPACPI_LOG | 156 | #define TPACPI_ERR KERN_ERR TPACPI_LOG |
@@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
905 | return 0; | 912 | return 0; |
906 | } | 913 | } |
907 | 914 | ||
915 | static int __init tpacpi_new_rfkill(const unsigned int id, | ||
916 | struct rfkill **rfk, | ||
917 | const enum rfkill_type rfktype, | ||
918 | const char *name, | ||
919 | int (*toggle_radio)(void *, enum rfkill_state), | ||
920 | int (*get_state)(void *, enum rfkill_state *)) | ||
921 | { | ||
922 | int res; | ||
923 | enum rfkill_state initial_state; | ||
924 | |||
925 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | ||
926 | if (!*rfk) { | ||
927 | printk(TPACPI_ERR | ||
928 | "failed to allocate memory for rfkill class\n"); | ||
929 | return -ENOMEM; | ||
930 | } | ||
931 | |||
932 | (*rfk)->name = name; | ||
933 | (*rfk)->get_state = get_state; | ||
934 | (*rfk)->toggle_radio = toggle_radio; | ||
935 | |||
936 | if (!get_state(NULL, &initial_state)) | ||
937 | (*rfk)->state = initial_state; | ||
938 | |||
939 | res = rfkill_register(*rfk); | ||
940 | if (res < 0) { | ||
941 | printk(TPACPI_ERR | ||
942 | "failed to register %s rfkill switch: %d\n", | ||
943 | name, res); | ||
944 | rfkill_free(*rfk); | ||
945 | *rfk = NULL; | ||
946 | return res; | ||
947 | } | ||
948 | |||
949 | return 0; | ||
950 | } | ||
951 | |||
908 | /************************************************************************* | 952 | /************************************************************************* |
909 | * thinkpad-acpi driver attributes | 953 | * thinkpad-acpi driver attributes |
910 | */ | 954 | */ |
@@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status) | |||
1285 | return 0; | 1329 | return 0; |
1286 | } | 1330 | } |
1287 | 1331 | ||
1288 | static void tpacpi_input_send_radiosw(void) | ||
1289 | { | ||
1290 | int wlsw; | ||
1291 | |||
1292 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | ||
1293 | mutex_lock(&tpacpi_inputdev_send_mutex); | ||
1294 | |||
1295 | input_report_switch(tpacpi_inputdev, | ||
1296 | SW_RADIO, !!wlsw); | ||
1297 | input_sync(tpacpi_inputdev); | ||
1298 | |||
1299 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
1300 | } | ||
1301 | } | ||
1302 | |||
1303 | static void tpacpi_input_send_tabletsw(void) | 1332 | static void tpacpi_input_send_tabletsw(void) |
1304 | { | 1333 | { |
1305 | int state; | 1334 | int state; |
@@ -1921,6 +1950,53 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
1921 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | 1950 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, |
1922 | }; | 1951 | }; |
1923 | 1952 | ||
1953 | static void bluetooth_update_rfk(void); | ||
1954 | static void wan_update_rfk(void); | ||
1955 | static void tpacpi_send_radiosw_update(void) | ||
1956 | { | ||
1957 | int wlsw; | ||
1958 | |||
1959 | /* Sync these BEFORE sending any rfkill events */ | ||
1960 | if (tp_features.bluetooth) | ||
1961 | bluetooth_update_rfk(); | ||
1962 | if (tp_features.wan) | ||
1963 | wan_update_rfk(); | ||
1964 | |||
1965 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | ||
1966 | mutex_lock(&tpacpi_inputdev_send_mutex); | ||
1967 | |||
1968 | input_report_switch(tpacpi_inputdev, | ||
1969 | SW_RFKILL_ALL, !!wlsw); | ||
1970 | input_sync(tpacpi_inputdev); | ||
1971 | |||
1972 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
1973 | } | ||
1974 | hotkey_radio_sw_notify_change(); | ||
1975 | } | ||
1976 | |||
1977 | static void hotkey_exit(void) | ||
1978 | { | ||
1979 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1980 | hotkey_poll_stop_sync(); | ||
1981 | #endif | ||
1982 | |||
1983 | if (hotkey_dev_attributes) | ||
1984 | delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); | ||
1985 | |||
1986 | kfree(hotkey_keycode_map); | ||
1987 | |||
1988 | if (tp_features.hotkey) { | ||
1989 | dbg_printk(TPACPI_DBG_EXIT, | ||
1990 | "restoring original hot key mask\n"); | ||
1991 | /* no short-circuit boolean operator below! */ | ||
1992 | if ((hotkey_mask_set(hotkey_orig_mask) | | ||
1993 | hotkey_status_set(hotkey_orig_status)) != 0) | ||
1994 | printk(TPACPI_ERR | ||
1995 | "failed to restore hot key mask " | ||
1996 | "to BIOS defaults\n"); | ||
1997 | } | ||
1998 | } | ||
1999 | |||
1924 | static int __init hotkey_init(struct ibm_init_struct *iibm) | 2000 | static int __init hotkey_init(struct ibm_init_struct *iibm) |
1925 | { | 2001 | { |
1926 | /* Requirements for changing the default keymaps: | 2002 | /* Requirements for changing the default keymaps: |
@@ -2060,226 +2136,221 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2060 | vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", | 2136 | vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", |
2061 | str_supported(tp_features.hotkey)); | 2137 | str_supported(tp_features.hotkey)); |
2062 | 2138 | ||
2063 | if (tp_features.hotkey) { | 2139 | if (!tp_features.hotkey) |
2064 | hotkey_dev_attributes = create_attr_set(13, NULL); | 2140 | return 1; |
2065 | if (!hotkey_dev_attributes) | ||
2066 | return -ENOMEM; | ||
2067 | res = add_many_to_attr_set(hotkey_dev_attributes, | ||
2068 | hotkey_attributes, | ||
2069 | ARRAY_SIZE(hotkey_attributes)); | ||
2070 | if (res) | ||
2071 | return res; | ||
2072 | 2141 | ||
2073 | /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, | 2142 | hotkey_dev_attributes = create_attr_set(13, NULL); |
2074 | A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking | 2143 | if (!hotkey_dev_attributes) |
2075 | for HKEY interface version 0x100 */ | 2144 | return -ENOMEM; |
2076 | if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { | 2145 | res = add_many_to_attr_set(hotkey_dev_attributes, |
2077 | if ((hkeyv >> 8) != 1) { | 2146 | hotkey_attributes, |
2078 | printk(TPACPI_ERR "unknown version of the " | 2147 | ARRAY_SIZE(hotkey_attributes)); |
2079 | "HKEY interface: 0x%x\n", hkeyv); | 2148 | if (res) |
2080 | printk(TPACPI_ERR "please report this to %s\n", | 2149 | goto err_exit; |
2081 | TPACPI_MAIL); | 2150 | |
2082 | } else { | 2151 | /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
2083 | /* | 2152 | A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking |
2084 | * MHKV 0x100 in A31, R40, R40e, | 2153 | for HKEY interface version 0x100 */ |
2085 | * T4x, X31, and later | 2154 | if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { |
2086 | */ | 2155 | if ((hkeyv >> 8) != 1) { |
2087 | tp_features.hotkey_mask = 1; | 2156 | printk(TPACPI_ERR "unknown version of the " |
2088 | } | 2157 | "HKEY interface: 0x%x\n", hkeyv); |
2158 | printk(TPACPI_ERR "please report this to %s\n", | ||
2159 | TPACPI_MAIL); | ||
2160 | } else { | ||
2161 | /* | ||
2162 | * MHKV 0x100 in A31, R40, R40e, | ||
2163 | * T4x, X31, and later | ||
2164 | */ | ||
2165 | tp_features.hotkey_mask = 1; | ||
2089 | } | 2166 | } |
2167 | } | ||
2090 | 2168 | ||
2091 | vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", | 2169 | vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", |
2092 | str_supported(tp_features.hotkey_mask)); | 2170 | str_supported(tp_features.hotkey_mask)); |
2093 | 2171 | ||
2094 | if (tp_features.hotkey_mask) { | 2172 | if (tp_features.hotkey_mask) { |
2095 | if (!acpi_evalf(hkey_handle, &hotkey_all_mask, | 2173 | if (!acpi_evalf(hkey_handle, &hotkey_all_mask, |
2096 | "MHKA", "qd")) { | 2174 | "MHKA", "qd")) { |
2097 | printk(TPACPI_ERR | 2175 | printk(TPACPI_ERR |
2098 | "missing MHKA handler, " | 2176 | "missing MHKA handler, " |
2099 | "please report this to %s\n", | 2177 | "please report this to %s\n", |
2100 | TPACPI_MAIL); | 2178 | TPACPI_MAIL); |
2101 | /* FN+F12, FN+F4, FN+F3 */ | 2179 | /* FN+F12, FN+F4, FN+F3 */ |
2102 | hotkey_all_mask = 0x080cU; | 2180 | hotkey_all_mask = 0x080cU; |
2103 | } | ||
2104 | } | 2181 | } |
2182 | } | ||
2105 | 2183 | ||
2106 | /* hotkey_source_mask *must* be zero for | 2184 | /* hotkey_source_mask *must* be zero for |
2107 | * the first hotkey_mask_get */ | 2185 | * the first hotkey_mask_get */ |
2108 | res = hotkey_status_get(&hotkey_orig_status); | 2186 | res = hotkey_status_get(&hotkey_orig_status); |
2109 | if (!res && tp_features.hotkey_mask) { | 2187 | if (res) |
2110 | res = hotkey_mask_get(); | 2188 | goto err_exit; |
2111 | hotkey_orig_mask = hotkey_mask; | 2189 | |
2112 | if (!res) { | 2190 | if (tp_features.hotkey_mask) { |
2113 | res = add_many_to_attr_set( | 2191 | res = hotkey_mask_get(); |
2114 | hotkey_dev_attributes, | 2192 | if (res) |
2115 | hotkey_mask_attributes, | 2193 | goto err_exit; |
2116 | ARRAY_SIZE(hotkey_mask_attributes)); | 2194 | |
2117 | } | 2195 | hotkey_orig_mask = hotkey_mask; |
2118 | } | 2196 | res = add_many_to_attr_set( |
2197 | hotkey_dev_attributes, | ||
2198 | hotkey_mask_attributes, | ||
2199 | ARRAY_SIZE(hotkey_mask_attributes)); | ||
2200 | if (res) | ||
2201 | goto err_exit; | ||
2202 | } | ||
2119 | 2203 | ||
2120 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | 2204 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL |
2121 | if (tp_features.hotkey_mask) { | 2205 | if (tp_features.hotkey_mask) { |
2122 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK | 2206 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK |
2123 | & ~hotkey_all_mask; | 2207 | & ~hotkey_all_mask; |
2124 | } else { | 2208 | } else { |
2125 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; | 2209 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; |
2126 | } | 2210 | } |
2127 | 2211 | ||
2128 | vdbg_printk(TPACPI_DBG_INIT, | 2212 | vdbg_printk(TPACPI_DBG_INIT, |
2129 | "hotkey source mask 0x%08x, polling freq %d\n", | 2213 | "hotkey source mask 0x%08x, polling freq %d\n", |
2130 | hotkey_source_mask, hotkey_poll_freq); | 2214 | hotkey_source_mask, hotkey_poll_freq); |
2131 | #endif | 2215 | #endif |
2132 | 2216 | ||
2133 | /* Not all thinkpads have a hardware radio switch */ | 2217 | /* Not all thinkpads have a hardware radio switch */ |
2134 | if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 2218 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
2135 | tp_features.hotkey_wlsw = 1; | 2219 | tp_features.hotkey_wlsw = 1; |
2136 | printk(TPACPI_INFO | 2220 | printk(TPACPI_INFO |
2137 | "radio switch found; radios are %s\n", | 2221 | "radio switch found; radios are %s\n", |
2138 | enabled(status, 0)); | 2222 | enabled(status, 0)); |
2139 | res = add_to_attr_set(hotkey_dev_attributes, | 2223 | } |
2140 | &dev_attr_hotkey_radio_sw.attr); | 2224 | if (tp_features.hotkey_wlsw) |
2141 | } | 2225 | res = add_to_attr_set(hotkey_dev_attributes, |
2226 | &dev_attr_hotkey_radio_sw.attr); | ||
2142 | 2227 | ||
2143 | /* For X41t, X60t, X61t Tablets... */ | 2228 | /* For X41t, X60t, X61t Tablets... */ |
2144 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 2229 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
2145 | tp_features.hotkey_tablet = 1; | 2230 | tp_features.hotkey_tablet = 1; |
2146 | printk(TPACPI_INFO | 2231 | printk(TPACPI_INFO |
2147 | "possible tablet mode switch found; " | 2232 | "possible tablet mode switch found; " |
2148 | "ThinkPad in %s mode\n", | 2233 | "ThinkPad in %s mode\n", |
2149 | (status & TP_HOTKEY_TABLET_MASK)? | 2234 | (status & TP_HOTKEY_TABLET_MASK)? |
2150 | "tablet" : "laptop"); | 2235 | "tablet" : "laptop"); |
2151 | res = add_to_attr_set(hotkey_dev_attributes, | 2236 | res = add_to_attr_set(hotkey_dev_attributes, |
2152 | &dev_attr_hotkey_tablet_mode.attr); | 2237 | &dev_attr_hotkey_tablet_mode.attr); |
2153 | } | 2238 | } |
2154 | 2239 | ||
2155 | if (!res) | 2240 | if (!res) |
2156 | res = register_attr_set_with_sysfs( | 2241 | res = register_attr_set_with_sysfs( |
2157 | hotkey_dev_attributes, | 2242 | hotkey_dev_attributes, |
2158 | &tpacpi_pdev->dev.kobj); | 2243 | &tpacpi_pdev->dev.kobj); |
2159 | if (res) | 2244 | if (res) |
2160 | return res; | 2245 | goto err_exit; |
2161 | 2246 | ||
2162 | /* Set up key map */ | 2247 | /* Set up key map */ |
2163 | 2248 | ||
2164 | hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, | 2249 | hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, |
2165 | GFP_KERNEL); | 2250 | GFP_KERNEL); |
2166 | if (!hotkey_keycode_map) { | 2251 | if (!hotkey_keycode_map) { |
2167 | printk(TPACPI_ERR | 2252 | printk(TPACPI_ERR |
2168 | "failed to allocate memory for key map\n"); | 2253 | "failed to allocate memory for key map\n"); |
2169 | return -ENOMEM; | 2254 | res = -ENOMEM; |
2170 | } | 2255 | goto err_exit; |
2256 | } | ||
2171 | 2257 | ||
2172 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | 2258 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { |
2173 | dbg_printk(TPACPI_DBG_INIT, | 2259 | dbg_printk(TPACPI_DBG_INIT, |
2174 | "using Lenovo default hot key map\n"); | 2260 | "using Lenovo default hot key map\n"); |
2175 | memcpy(hotkey_keycode_map, &lenovo_keycode_map, | 2261 | memcpy(hotkey_keycode_map, &lenovo_keycode_map, |
2176 | TPACPI_HOTKEY_MAP_SIZE); | 2262 | TPACPI_HOTKEY_MAP_SIZE); |
2263 | } else { | ||
2264 | dbg_printk(TPACPI_DBG_INIT, | ||
2265 | "using IBM default hot key map\n"); | ||
2266 | memcpy(hotkey_keycode_map, &ibm_keycode_map, | ||
2267 | TPACPI_HOTKEY_MAP_SIZE); | ||
2268 | } | ||
2269 | |||
2270 | set_bit(EV_KEY, tpacpi_inputdev->evbit); | ||
2271 | set_bit(EV_MSC, tpacpi_inputdev->evbit); | ||
2272 | set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); | ||
2273 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; | ||
2274 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; | ||
2275 | tpacpi_inputdev->keycode = hotkey_keycode_map; | ||
2276 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { | ||
2277 | if (hotkey_keycode_map[i] != KEY_RESERVED) { | ||
2278 | set_bit(hotkey_keycode_map[i], | ||
2279 | tpacpi_inputdev->keybit); | ||
2177 | } else { | 2280 | } else { |
2178 | dbg_printk(TPACPI_DBG_INIT, | 2281 | if (i < sizeof(hotkey_reserved_mask)*8) |
2179 | "using IBM default hot key map\n"); | 2282 | hotkey_reserved_mask |= 1 << i; |
2180 | memcpy(hotkey_keycode_map, &ibm_keycode_map, | ||
2181 | TPACPI_HOTKEY_MAP_SIZE); | ||
2182 | } | ||
2183 | |||
2184 | set_bit(EV_KEY, tpacpi_inputdev->evbit); | ||
2185 | set_bit(EV_MSC, tpacpi_inputdev->evbit); | ||
2186 | set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); | ||
2187 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; | ||
2188 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; | ||
2189 | tpacpi_inputdev->keycode = hotkey_keycode_map; | ||
2190 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { | ||
2191 | if (hotkey_keycode_map[i] != KEY_RESERVED) { | ||
2192 | set_bit(hotkey_keycode_map[i], | ||
2193 | tpacpi_inputdev->keybit); | ||
2194 | } else { | ||
2195 | if (i < sizeof(hotkey_reserved_mask)*8) | ||
2196 | hotkey_reserved_mask |= 1 << i; | ||
2197 | } | ||
2198 | } | 2283 | } |
2284 | } | ||
2199 | 2285 | ||
2200 | if (tp_features.hotkey_wlsw) { | 2286 | if (tp_features.hotkey_wlsw) { |
2201 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 2287 | set_bit(EV_SW, tpacpi_inputdev->evbit); |
2202 | set_bit(SW_RADIO, tpacpi_inputdev->swbit); | 2288 | set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit); |
2203 | } | 2289 | } |
2204 | if (tp_features.hotkey_tablet) { | 2290 | if (tp_features.hotkey_tablet) { |
2205 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 2291 | set_bit(EV_SW, tpacpi_inputdev->evbit); |
2206 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 2292 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); |
2207 | } | 2293 | } |
2208 | 2294 | ||
2209 | /* Do not issue duplicate brightness change events to | 2295 | /* Do not issue duplicate brightness change events to |
2210 | * userspace */ | 2296 | * userspace */ |
2211 | if (!tp_features.bright_acpimode) | 2297 | if (!tp_features.bright_acpimode) |
2212 | /* update bright_acpimode... */ | 2298 | /* update bright_acpimode... */ |
2213 | tpacpi_check_std_acpi_brightness_support(); | 2299 | tpacpi_check_std_acpi_brightness_support(); |
2214 | |||
2215 | if (tp_features.bright_acpimode) { | ||
2216 | printk(TPACPI_INFO | ||
2217 | "This ThinkPad has standard ACPI backlight " | ||
2218 | "brightness control, supported by the ACPI " | ||
2219 | "video driver\n"); | ||
2220 | printk(TPACPI_NOTICE | ||
2221 | "Disabling thinkpad-acpi brightness events " | ||
2222 | "by default...\n"); | ||
2223 | |||
2224 | /* The hotkey_reserved_mask change below is not | ||
2225 | * necessary while the keys are at KEY_RESERVED in the | ||
2226 | * default map, but better safe than sorry, leave it | ||
2227 | * here as a marker of what we have to do, especially | ||
2228 | * when we finally become able to set this at runtime | ||
2229 | * on response to X.org requests */ | ||
2230 | hotkey_reserved_mask |= | ||
2231 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | ||
2232 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); | ||
2233 | } | ||
2234 | 2300 | ||
2235 | dbg_printk(TPACPI_DBG_INIT, | 2301 | if (tp_features.bright_acpimode) { |
2236 | "enabling hot key handling\n"); | 2302 | printk(TPACPI_INFO |
2237 | res = hotkey_status_set(1); | 2303 | "This ThinkPad has standard ACPI backlight " |
2238 | if (res) | 2304 | "brightness control, supported by the ACPI " |
2239 | return res; | 2305 | "video driver\n"); |
2240 | res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask) | 2306 | printk(TPACPI_NOTICE |
2241 | & ~hotkey_reserved_mask) | 2307 | "Disabling thinkpad-acpi brightness events " |
2242 | | hotkey_orig_mask); | 2308 | "by default...\n"); |
2243 | if (res < 0 && res != -ENXIO) | 2309 | |
2244 | return res; | 2310 | /* The hotkey_reserved_mask change below is not |
2311 | * necessary while the keys are at KEY_RESERVED in the | ||
2312 | * default map, but better safe than sorry, leave it | ||
2313 | * here as a marker of what we have to do, especially | ||
2314 | * when we finally become able to set this at runtime | ||
2315 | * on response to X.org requests */ | ||
2316 | hotkey_reserved_mask |= | ||
2317 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | ||
2318 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); | ||
2319 | } | ||
2320 | |||
2321 | dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n"); | ||
2322 | res = hotkey_status_set(1); | ||
2323 | if (res) { | ||
2324 | hotkey_exit(); | ||
2325 | return res; | ||
2326 | } | ||
2327 | res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask) | ||
2328 | & ~hotkey_reserved_mask) | ||
2329 | | hotkey_orig_mask); | ||
2330 | if (res < 0 && res != -ENXIO) { | ||
2331 | hotkey_exit(); | ||
2332 | return res; | ||
2333 | } | ||
2245 | 2334 | ||
2246 | dbg_printk(TPACPI_DBG_INIT, | 2335 | dbg_printk(TPACPI_DBG_INIT, |
2247 | "legacy hot key reporting over procfs %s\n", | 2336 | "legacy hot key reporting over procfs %s\n", |
2248 | (hotkey_report_mode < 2) ? | 2337 | (hotkey_report_mode < 2) ? |
2249 | "enabled" : "disabled"); | 2338 | "enabled" : "disabled"); |
2250 | 2339 | ||
2251 | tpacpi_inputdev->open = &hotkey_inputdev_open; | 2340 | tpacpi_inputdev->open = &hotkey_inputdev_open; |
2252 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 2341 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
2253 | 2342 | ||
2254 | hotkey_poll_setup_safe(1); | 2343 | hotkey_poll_setup_safe(1); |
2255 | tpacpi_input_send_radiosw(); | 2344 | tpacpi_send_radiosw_update(); |
2256 | tpacpi_input_send_tabletsw(); | 2345 | tpacpi_input_send_tabletsw(); |
2257 | } | ||
2258 | 2346 | ||
2259 | return (tp_features.hotkey)? 0 : 1; | 2347 | return 0; |
2260 | } | ||
2261 | 2348 | ||
2262 | static void hotkey_exit(void) | 2349 | err_exit: |
2263 | { | 2350 | delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); |
2264 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | 2351 | hotkey_dev_attributes = NULL; |
2265 | hotkey_poll_stop_sync(); | ||
2266 | #endif | ||
2267 | 2352 | ||
2268 | if (tp_features.hotkey) { | 2353 | return (res < 0)? res : 1; |
2269 | dbg_printk(TPACPI_DBG_EXIT, | ||
2270 | "restoring original hot key mask\n"); | ||
2271 | /* no short-circuit boolean operator below! */ | ||
2272 | if ((hotkey_mask_set(hotkey_orig_mask) | | ||
2273 | hotkey_status_set(hotkey_orig_status)) != 0) | ||
2274 | printk(TPACPI_ERR | ||
2275 | "failed to restore hot key mask " | ||
2276 | "to BIOS defaults\n"); | ||
2277 | } | ||
2278 | |||
2279 | if (hotkey_dev_attributes) { | ||
2280 | delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); | ||
2281 | hotkey_dev_attributes = NULL; | ||
2282 | } | ||
2283 | } | 2354 | } |
2284 | 2355 | ||
2285 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 2356 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
@@ -2402,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2402 | case 7: | 2473 | case 7: |
2403 | /* 0x7000-0x7FFF: misc */ | 2474 | /* 0x7000-0x7FFF: misc */ |
2404 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { | 2475 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { |
2405 | tpacpi_input_send_radiosw(); | 2476 | tpacpi_send_radiosw_update(); |
2406 | hotkey_radio_sw_notify_change(); | ||
2407 | send_acpi_ev = 0; | 2477 | send_acpi_ev = 0; |
2408 | break; | 2478 | break; |
2409 | } | 2479 | } |
@@ -2446,8 +2516,7 @@ static void hotkey_resume(void) | |||
2446 | printk(TPACPI_ERR | 2516 | printk(TPACPI_ERR |
2447 | "error while trying to read hot key mask " | 2517 | "error while trying to read hot key mask " |
2448 | "from firmware\n"); | 2518 | "from firmware\n"); |
2449 | tpacpi_input_send_radiosw(); | 2519 | tpacpi_send_radiosw_update(); |
2450 | hotkey_radio_sw_notify_change(); | ||
2451 | hotkey_tablet_mode_notify_change(); | 2520 | hotkey_tablet_mode_notify_change(); |
2452 | hotkey_wakeup_reason_notify_change(); | 2521 | hotkey_wakeup_reason_notify_change(); |
2453 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2522 | hotkey_wakeup_hotunplug_complete_notify_change(); |
@@ -2564,8 +2633,66 @@ enum { | |||
2564 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ | 2633 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ |
2565 | }; | 2634 | }; |
2566 | 2635 | ||
2567 | static int bluetooth_get_radiosw(void); | 2636 | static struct rfkill *tpacpi_bluetooth_rfkill; |
2568 | static int bluetooth_set_radiosw(int radio_on); | 2637 | |
2638 | static int bluetooth_get_radiosw(void) | ||
2639 | { | ||
2640 | int status; | ||
2641 | |||
2642 | if (!tp_features.bluetooth) | ||
2643 | return -ENODEV; | ||
2644 | |||
2645 | /* WLSW overrides bluetooth in firmware/hardware, reflect that */ | ||
2646 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
2647 | return RFKILL_STATE_HARD_BLOCKED; | ||
2648 | |||
2649 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
2650 | return -EIO; | ||
2651 | |||
2652 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? | ||
2653 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
2654 | } | ||
2655 | |||
2656 | static void bluetooth_update_rfk(void) | ||
2657 | { | ||
2658 | int status; | ||
2659 | |||
2660 | if (!tpacpi_bluetooth_rfkill) | ||
2661 | return; | ||
2662 | |||
2663 | status = bluetooth_get_radiosw(); | ||
2664 | if (status < 0) | ||
2665 | return; | ||
2666 | rfkill_force_state(tpacpi_bluetooth_rfkill, status); | ||
2667 | } | ||
2668 | |||
2669 | static int bluetooth_set_radiosw(int radio_on, int update_rfk) | ||
2670 | { | ||
2671 | int status; | ||
2672 | |||
2673 | if (!tp_features.bluetooth) | ||
2674 | return -ENODEV; | ||
2675 | |||
2676 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
2677 | * reason to risk weird behaviour. */ | ||
2678 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
2679 | && radio_on) | ||
2680 | return -EPERM; | ||
2681 | |||
2682 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
2683 | return -EIO; | ||
2684 | if (radio_on) | ||
2685 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | ||
2686 | else | ||
2687 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | ||
2688 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | ||
2689 | return -EIO; | ||
2690 | |||
2691 | if (update_rfk) | ||
2692 | bluetooth_update_rfk(); | ||
2693 | |||
2694 | return 0; | ||
2695 | } | ||
2569 | 2696 | ||
2570 | /* sysfs bluetooth enable ---------------------------------------------- */ | 2697 | /* sysfs bluetooth enable ---------------------------------------------- */ |
2571 | static ssize_t bluetooth_enable_show(struct device *dev, | 2698 | static ssize_t bluetooth_enable_show(struct device *dev, |
@@ -2578,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev, | |||
2578 | if (status < 0) | 2705 | if (status < 0) |
2579 | return status; | 2706 | return status; |
2580 | 2707 | ||
2581 | return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); | 2708 | return snprintf(buf, PAGE_SIZE, "%d\n", |
2709 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
2582 | } | 2710 | } |
2583 | 2711 | ||
2584 | static ssize_t bluetooth_enable_store(struct device *dev, | 2712 | static ssize_t bluetooth_enable_store(struct device *dev, |
@@ -2591,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev, | |||
2591 | if (parse_strtoul(buf, 1, &t)) | 2719 | if (parse_strtoul(buf, 1, &t)) |
2592 | return -EINVAL; | 2720 | return -EINVAL; |
2593 | 2721 | ||
2594 | res = bluetooth_set_radiosw(t); | 2722 | res = bluetooth_set_radiosw(t, 1); |
2595 | 2723 | ||
2596 | return (res) ? res : count; | 2724 | return (res) ? res : count; |
2597 | } | 2725 | } |
@@ -2611,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = { | |||
2611 | .attrs = bluetooth_attributes, | 2739 | .attrs = bluetooth_attributes, |
2612 | }; | 2740 | }; |
2613 | 2741 | ||
2742 | static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) | ||
2743 | { | ||
2744 | int bts = bluetooth_get_radiosw(); | ||
2745 | |||
2746 | if (bts < 0) | ||
2747 | return bts; | ||
2748 | |||
2749 | *state = bts; | ||
2750 | return 0; | ||
2751 | } | ||
2752 | |||
2753 | static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | ||
2754 | { | ||
2755 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
2756 | } | ||
2757 | |||
2758 | static void bluetooth_exit(void) | ||
2759 | { | ||
2760 | if (tpacpi_bluetooth_rfkill) | ||
2761 | rfkill_unregister(tpacpi_bluetooth_rfkill); | ||
2762 | |||
2763 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2764 | &bluetooth_attr_group); | ||
2765 | } | ||
2766 | |||
2614 | static int __init bluetooth_init(struct ibm_init_struct *iibm) | 2767 | static int __init bluetooth_init(struct ibm_init_struct *iibm) |
2615 | { | 2768 | { |
2616 | int res; | 2769 | int res; |
@@ -2629,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
2629 | str_supported(tp_features.bluetooth), | 2782 | str_supported(tp_features.bluetooth), |
2630 | status); | 2783 | status); |
2631 | 2784 | ||
2632 | if (tp_features.bluetooth) { | 2785 | if (tp_features.bluetooth && |
2633 | if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { | 2786 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { |
2634 | /* no bluetooth hardware present in system */ | 2787 | /* no bluetooth hardware present in system */ |
2635 | tp_features.bluetooth = 0; | 2788 | tp_features.bluetooth = 0; |
2636 | dbg_printk(TPACPI_DBG_INIT, | 2789 | dbg_printk(TPACPI_DBG_INIT, |
2637 | "bluetooth hardware not installed\n"); | 2790 | "bluetooth hardware not installed\n"); |
2638 | } else { | ||
2639 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
2640 | &bluetooth_attr_group); | ||
2641 | if (res) | ||
2642 | return res; | ||
2643 | } | ||
2644 | } | 2791 | } |
2645 | 2792 | ||
2646 | return (tp_features.bluetooth)? 0 : 1; | ||
2647 | } | ||
2648 | |||
2649 | static void bluetooth_exit(void) | ||
2650 | { | ||
2651 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2652 | &bluetooth_attr_group); | ||
2653 | } | ||
2654 | |||
2655 | static int bluetooth_get_radiosw(void) | ||
2656 | { | ||
2657 | int status; | ||
2658 | |||
2659 | if (!tp_features.bluetooth) | 2793 | if (!tp_features.bluetooth) |
2660 | return -ENODEV; | 2794 | return 1; |
2661 | |||
2662 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
2663 | return -EIO; | ||
2664 | |||
2665 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); | ||
2666 | } | ||
2667 | |||
2668 | static int bluetooth_set_radiosw(int radio_on) | ||
2669 | { | ||
2670 | int status; | ||
2671 | 2795 | ||
2672 | if (!tp_features.bluetooth) | 2796 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, |
2673 | return -ENODEV; | 2797 | &bluetooth_attr_group); |
2798 | if (res) | ||
2799 | return res; | ||
2674 | 2800 | ||
2675 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 2801 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, |
2676 | return -EIO; | 2802 | &tpacpi_bluetooth_rfkill, |
2677 | if (radio_on) | 2803 | RFKILL_TYPE_BLUETOOTH, |
2678 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | 2804 | "tpacpi_bluetooth_sw", |
2679 | else | 2805 | tpacpi_bluetooth_rfk_set, |
2680 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | 2806 | tpacpi_bluetooth_rfk_get); |
2681 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 2807 | if (res) { |
2682 | return -EIO; | 2808 | bluetooth_exit(); |
2809 | return res; | ||
2810 | } | ||
2683 | 2811 | ||
2684 | return 0; | 2812 | return 0; |
2685 | } | 2813 | } |
@@ -2694,7 +2822,8 @@ static int bluetooth_read(char *p) | |||
2694 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 2822 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
2695 | else { | 2823 | else { |
2696 | len += sprintf(p + len, "status:\t\t%s\n", | 2824 | len += sprintf(p + len, "status:\t\t%s\n", |
2697 | (status)? "enabled" : "disabled"); | 2825 | (status == RFKILL_STATE_UNBLOCKED) ? |
2826 | "enabled" : "disabled"); | ||
2698 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 2827 | len += sprintf(p + len, "commands:\tenable, disable\n"); |
2699 | } | 2828 | } |
2700 | 2829 | ||
@@ -2710,9 +2839,9 @@ static int bluetooth_write(char *buf) | |||
2710 | 2839 | ||
2711 | while ((cmd = next_cmd(&buf))) { | 2840 | while ((cmd = next_cmd(&buf))) { |
2712 | if (strlencmp(cmd, "enable") == 0) { | 2841 | if (strlencmp(cmd, "enable") == 0) { |
2713 | bluetooth_set_radiosw(1); | 2842 | bluetooth_set_radiosw(1, 1); |
2714 | } else if (strlencmp(cmd, "disable") == 0) { | 2843 | } else if (strlencmp(cmd, "disable") == 0) { |
2715 | bluetooth_set_radiosw(0); | 2844 | bluetooth_set_radiosw(0, 1); |
2716 | } else | 2845 | } else |
2717 | return -EINVAL; | 2846 | return -EINVAL; |
2718 | } | 2847 | } |
@@ -2738,8 +2867,66 @@ enum { | |||
2738 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ | 2867 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ |
2739 | }; | 2868 | }; |
2740 | 2869 | ||
2741 | static int wan_get_radiosw(void); | 2870 | static struct rfkill *tpacpi_wan_rfkill; |
2742 | static int wan_set_radiosw(int radio_on); | 2871 | |
2872 | static int wan_get_radiosw(void) | ||
2873 | { | ||
2874 | int status; | ||
2875 | |||
2876 | if (!tp_features.wan) | ||
2877 | return -ENODEV; | ||
2878 | |||
2879 | /* WLSW overrides WWAN in firmware/hardware, reflect that */ | ||
2880 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
2881 | return RFKILL_STATE_HARD_BLOCKED; | ||
2882 | |||
2883 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
2884 | return -EIO; | ||
2885 | |||
2886 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? | ||
2887 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
2888 | } | ||
2889 | |||
2890 | static void wan_update_rfk(void) | ||
2891 | { | ||
2892 | int status; | ||
2893 | |||
2894 | if (!tpacpi_wan_rfkill) | ||
2895 | return; | ||
2896 | |||
2897 | status = wan_get_radiosw(); | ||
2898 | if (status < 0) | ||
2899 | return; | ||
2900 | rfkill_force_state(tpacpi_wan_rfkill, status); | ||
2901 | } | ||
2902 | |||
2903 | static int wan_set_radiosw(int radio_on, int update_rfk) | ||
2904 | { | ||
2905 | int status; | ||
2906 | |||
2907 | if (!tp_features.wan) | ||
2908 | return -ENODEV; | ||
2909 | |||
2910 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
2911 | * reason to risk weird behaviour. */ | ||
2912 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
2913 | && radio_on) | ||
2914 | return -EPERM; | ||
2915 | |||
2916 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
2917 | return -EIO; | ||
2918 | if (radio_on) | ||
2919 | status |= TP_ACPI_WANCARD_RADIOSSW; | ||
2920 | else | ||
2921 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | ||
2922 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | ||
2923 | return -EIO; | ||
2924 | |||
2925 | if (update_rfk) | ||
2926 | wan_update_rfk(); | ||
2927 | |||
2928 | return 0; | ||
2929 | } | ||
2743 | 2930 | ||
2744 | /* sysfs wan enable ---------------------------------------------------- */ | 2931 | /* sysfs wan enable ---------------------------------------------------- */ |
2745 | static ssize_t wan_enable_show(struct device *dev, | 2932 | static ssize_t wan_enable_show(struct device *dev, |
@@ -2752,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev, | |||
2752 | if (status < 0) | 2939 | if (status < 0) |
2753 | return status; | 2940 | return status; |
2754 | 2941 | ||
2755 | return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); | 2942 | return snprintf(buf, PAGE_SIZE, "%d\n", |
2943 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
2756 | } | 2944 | } |
2757 | 2945 | ||
2758 | static ssize_t wan_enable_store(struct device *dev, | 2946 | static ssize_t wan_enable_store(struct device *dev, |
@@ -2765,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev, | |||
2765 | if (parse_strtoul(buf, 1, &t)) | 2953 | if (parse_strtoul(buf, 1, &t)) |
2766 | return -EINVAL; | 2954 | return -EINVAL; |
2767 | 2955 | ||
2768 | res = wan_set_radiosw(t); | 2956 | res = wan_set_radiosw(t, 1); |
2769 | 2957 | ||
2770 | return (res) ? res : count; | 2958 | return (res) ? res : count; |
2771 | } | 2959 | } |
@@ -2785,6 +2973,31 @@ static const struct attribute_group wan_attr_group = { | |||
2785 | .attrs = wan_attributes, | 2973 | .attrs = wan_attributes, |
2786 | }; | 2974 | }; |
2787 | 2975 | ||
2976 | static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) | ||
2977 | { | ||
2978 | int wans = wan_get_radiosw(); | ||
2979 | |||
2980 | if (wans < 0) | ||
2981 | return wans; | ||
2982 | |||
2983 | *state = wans; | ||
2984 | return 0; | ||
2985 | } | ||
2986 | |||
2987 | static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | ||
2988 | { | ||
2989 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
2990 | } | ||
2991 | |||
2992 | static void wan_exit(void) | ||
2993 | { | ||
2994 | if (tpacpi_wan_rfkill) | ||
2995 | rfkill_unregister(tpacpi_wan_rfkill); | ||
2996 | |||
2997 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2998 | &wan_attr_group); | ||
2999 | } | ||
3000 | |||
2788 | static int __init wan_init(struct ibm_init_struct *iibm) | 3001 | static int __init wan_init(struct ibm_init_struct *iibm) |
2789 | { | 3002 | { |
2790 | int res; | 3003 | int res; |
@@ -2801,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
2801 | str_supported(tp_features.wan), | 3014 | str_supported(tp_features.wan), |
2802 | status); | 3015 | status); |
2803 | 3016 | ||
2804 | if (tp_features.wan) { | 3017 | if (tp_features.wan && |
2805 | if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { | 3018 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { |
2806 | /* no wan hardware present in system */ | 3019 | /* no wan hardware present in system */ |
2807 | tp_features.wan = 0; | 3020 | tp_features.wan = 0; |
2808 | dbg_printk(TPACPI_DBG_INIT, | 3021 | dbg_printk(TPACPI_DBG_INIT, |
2809 | "wan hardware not installed\n"); | 3022 | "wan hardware not installed\n"); |
2810 | } else { | ||
2811 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
2812 | &wan_attr_group); | ||
2813 | if (res) | ||
2814 | return res; | ||
2815 | } | ||
2816 | } | 3023 | } |
2817 | 3024 | ||
2818 | return (tp_features.wan)? 0 : 1; | ||
2819 | } | ||
2820 | |||
2821 | static void wan_exit(void) | ||
2822 | { | ||
2823 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
2824 | &wan_attr_group); | ||
2825 | } | ||
2826 | |||
2827 | static int wan_get_radiosw(void) | ||
2828 | { | ||
2829 | int status; | ||
2830 | |||
2831 | if (!tp_features.wan) | 3025 | if (!tp_features.wan) |
2832 | return -ENODEV; | 3026 | return 1; |
2833 | |||
2834 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
2835 | return -EIO; | ||
2836 | |||
2837 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); | ||
2838 | } | ||
2839 | |||
2840 | static int wan_set_radiosw(int radio_on) | ||
2841 | { | ||
2842 | int status; | ||
2843 | 3027 | ||
2844 | if (!tp_features.wan) | 3028 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, |
2845 | return -ENODEV; | 3029 | &wan_attr_group); |
3030 | if (res) | ||
3031 | return res; | ||
2846 | 3032 | ||
2847 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3033 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, |
2848 | return -EIO; | 3034 | &tpacpi_wan_rfkill, |
2849 | if (radio_on) | 3035 | RFKILL_TYPE_WWAN, |
2850 | status |= TP_ACPI_WANCARD_RADIOSSW; | 3036 | "tpacpi_wwan_sw", |
2851 | else | 3037 | tpacpi_wan_rfk_set, |
2852 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | 3038 | tpacpi_wan_rfk_get); |
2853 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3039 | if (res) { |
2854 | return -EIO; | 3040 | wan_exit(); |
3041 | return res; | ||
3042 | } | ||
2855 | 3043 | ||
2856 | return 0; | 3044 | return 0; |
2857 | } | 3045 | } |
@@ -2866,7 +3054,8 @@ static int wan_read(char *p) | |||
2866 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3054 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
2867 | else { | 3055 | else { |
2868 | len += sprintf(p + len, "status:\t\t%s\n", | 3056 | len += sprintf(p + len, "status:\t\t%s\n", |
2869 | (status)? "enabled" : "disabled"); | 3057 | (status == RFKILL_STATE_UNBLOCKED) ? |
3058 | "enabled" : "disabled"); | ||
2870 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 3059 | len += sprintf(p + len, "commands:\tenable, disable\n"); |
2871 | } | 3060 | } |
2872 | 3061 | ||
@@ -2882,9 +3071,9 @@ static int wan_write(char *buf) | |||
2882 | 3071 | ||
2883 | while ((cmd = next_cmd(&buf))) { | 3072 | while ((cmd = next_cmd(&buf))) { |
2884 | if (strlencmp(cmd, "enable") == 0) { | 3073 | if (strlencmp(cmd, "enable") == 0) { |
2885 | wan_set_radiosw(1); | 3074 | wan_set_radiosw(1, 1); |
2886 | } else if (strlencmp(cmd, "disable") == 0) { | 3075 | } else if (strlencmp(cmd, "disable") == 0) { |
2887 | wan_set_radiosw(0); | 3076 | wan_set_radiosw(0, 1); |
2888 | } else | 3077 | } else |
2889 | return -EINVAL; | 3078 | return -EINVAL; |
2890 | } | 3079 | } |
@@ -3319,7 +3508,7 @@ static struct tpacpi_led_classdev tpacpi_led_thinklight = { | |||
3319 | 3508 | ||
3320 | static int __init light_init(struct ibm_init_struct *iibm) | 3509 | static int __init light_init(struct ibm_init_struct *iibm) |
3321 | { | 3510 | { |
3322 | int rc = 0; | 3511 | int rc; |
3323 | 3512 | ||
3324 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); | 3513 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); |
3325 | 3514 | ||
@@ -3337,20 +3526,23 @@ static int __init light_init(struct ibm_init_struct *iibm) | |||
3337 | tp_features.light_status = | 3526 | tp_features.light_status = |
3338 | acpi_evalf(ec_handle, NULL, "KBLT", "qv"); | 3527 | acpi_evalf(ec_handle, NULL, "KBLT", "qv"); |
3339 | 3528 | ||
3340 | vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", | 3529 | vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n", |
3341 | str_supported(tp_features.light)); | 3530 | str_supported(tp_features.light), |
3531 | str_supported(tp_features.light_status)); | ||
3342 | 3532 | ||
3343 | if (tp_features.light) { | 3533 | if (!tp_features.light) |
3344 | rc = led_classdev_register(&tpacpi_pdev->dev, | 3534 | return 1; |
3345 | &tpacpi_led_thinklight.led_classdev); | 3535 | |
3346 | } | 3536 | rc = led_classdev_register(&tpacpi_pdev->dev, |
3537 | &tpacpi_led_thinklight.led_classdev); | ||
3347 | 3538 | ||
3348 | if (rc < 0) { | 3539 | if (rc < 0) { |
3349 | tp_features.light = 0; | 3540 | tp_features.light = 0; |
3350 | tp_features.light_status = 0; | 3541 | tp_features.light_status = 0; |
3351 | } else { | 3542 | } else { |
3352 | rc = (tp_features.light)? 0 : 1; | 3543 | rc = 0; |
3353 | } | 3544 | } |
3545 | |||
3354 | return rc; | 3546 | return rc; |
3355 | } | 3547 | } |
3356 | 3548 | ||
@@ -3833,7 +4025,7 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | |||
3833 | "tpacpi::standby", | 4025 | "tpacpi::standby", |
3834 | }; | 4026 | }; |
3835 | 4027 | ||
3836 | static int led_get_status(unsigned int led) | 4028 | static int led_get_status(const unsigned int led) |
3837 | { | 4029 | { |
3838 | int status; | 4030 | int status; |
3839 | enum led_status_t led_s; | 4031 | enum led_status_t led_s; |
@@ -3857,41 +4049,42 @@ static int led_get_status(unsigned int led) | |||
3857 | /* not reached */ | 4049 | /* not reached */ |
3858 | } | 4050 | } |
3859 | 4051 | ||
3860 | static int led_set_status(unsigned int led, enum led_status_t ledstatus) | 4052 | static int led_set_status(const unsigned int led, |
4053 | const enum led_status_t ledstatus) | ||
3861 | { | 4054 | { |
3862 | /* off, on, blink. Index is led_status_t */ | 4055 | /* off, on, blink. Index is led_status_t */ |
3863 | static const int led_sled_arg1[] = { 0, 1, 3 }; | 4056 | static const unsigned int led_sled_arg1[] = { 0, 1, 3 }; |
3864 | static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | 4057 | static const unsigned int led_led_arg1[] = { 0, 0x80, 0xc0 }; |
3865 | static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
3866 | static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
3867 | 4058 | ||
3868 | int rc = 0; | 4059 | int rc = 0; |
3869 | 4060 | ||
3870 | switch (led_supported) { | 4061 | switch (led_supported) { |
3871 | case TPACPI_LED_570: | 4062 | case TPACPI_LED_570: |
3872 | /* 570 */ | 4063 | /* 570 */ |
3873 | led = 1 << led; | 4064 | if (led > 7) |
3874 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | 4065 | return -EINVAL; |
3875 | led, led_sled_arg1[ledstatus])) | 4066 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
3876 | rc = -EIO; | 4067 | (1 << led), led_sled_arg1[ledstatus])) |
3877 | break; | 4068 | rc = -EIO; |
4069 | break; | ||
3878 | case TPACPI_LED_OLD: | 4070 | case TPACPI_LED_OLD: |
3879 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | 4071 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ |
3880 | led = 1 << led; | 4072 | if (led > 7) |
3881 | rc = ec_write(TPACPI_LED_EC_HLMS, led); | 4073 | return -EINVAL; |
3882 | if (rc >= 0) | 4074 | rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led)); |
3883 | rc = ec_write(TPACPI_LED_EC_HLBL, | 4075 | if (rc >= 0) |
3884 | led * led_exp_hlbl[ledstatus]); | 4076 | rc = ec_write(TPACPI_LED_EC_HLBL, |
3885 | if (rc >= 0) | 4077 | (ledstatus == TPACPI_LED_BLINK) << led); |
3886 | rc = ec_write(TPACPI_LED_EC_HLCL, | 4078 | if (rc >= 0) |
3887 | led * led_exp_hlcl[ledstatus]); | 4079 | rc = ec_write(TPACPI_LED_EC_HLCL, |
3888 | break; | 4080 | (ledstatus != TPACPI_LED_OFF) << led); |
4081 | break; | ||
3889 | case TPACPI_LED_NEW: | 4082 | case TPACPI_LED_NEW: |
3890 | /* all others */ | 4083 | /* all others */ |
3891 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | 4084 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
3892 | led, led_led_arg1[ledstatus])) | 4085 | led, led_led_arg1[ledstatus])) |
3893 | rc = -EIO; | 4086 | rc = -EIO; |
3894 | break; | 4087 | break; |
3895 | default: | 4088 | default: |
3896 | rc = -ENXIO; | 4089 | rc = -ENXIO; |
3897 | } | 4090 | } |
@@ -3978,7 +4171,6 @@ static void led_exit(void) | |||
3978 | } | 4171 | } |
3979 | 4172 | ||
3980 | kfree(tpacpi_leds); | 4173 | kfree(tpacpi_leds); |
3981 | tpacpi_leds = NULL; | ||
3982 | } | 4174 | } |
3983 | 4175 | ||
3984 | static int __init led_init(struct ibm_init_struct *iibm) | 4176 | static int __init led_init(struct ibm_init_struct *iibm) |
@@ -4802,7 +4994,6 @@ static void brightness_exit(void) | |||
4802 | vdbg_printk(TPACPI_DBG_EXIT, | 4994 | vdbg_printk(TPACPI_DBG_EXIT, |
4803 | "calling backlight_device_unregister()\n"); | 4995 | "calling backlight_device_unregister()\n"); |
4804 | backlight_device_unregister(ibm_backlight_device); | 4996 | backlight_device_unregister(ibm_backlight_device); |
4805 | ibm_backlight_device = NULL; | ||
4806 | } | 4997 | } |
4807 | } | 4998 | } |
4808 | 4999 | ||
@@ -5764,11 +5955,16 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
5764 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { | 5955 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { |
5765 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, | 5956 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, |
5766 | &fan_attr_group); | 5957 | &fan_attr_group); |
5767 | if (!(rc < 0)) | ||
5768 | rc = driver_create_file(&tpacpi_hwmon_pdriver.driver, | ||
5769 | &driver_attr_fan_watchdog); | ||
5770 | if (rc < 0) | 5958 | if (rc < 0) |
5771 | return rc; | 5959 | return rc; |
5960 | |||
5961 | rc = driver_create_file(&tpacpi_hwmon_pdriver.driver, | ||
5962 | &driver_attr_fan_watchdog); | ||
5963 | if (rc < 0) { | ||
5964 | sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, | ||
5965 | &fan_attr_group); | ||
5966 | return rc; | ||
5967 | } | ||
5772 | return 0; | 5968 | return 0; |
5773 | } else | 5969 | } else |
5774 | return 1; | 5970 | return 1; |
@@ -6144,13 +6340,18 @@ err_out: | |||
6144 | 6340 | ||
6145 | /* Probing */ | 6341 | /* Probing */ |
6146 | 6342 | ||
6147 | static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | 6343 | /* returns 0 - probe ok, or < 0 - probe error. |
6344 | * Probe ok doesn't mean thinkpad found. | ||
6345 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ | ||
6346 | static int __must_check __init get_thinkpad_model_data( | ||
6347 | struct thinkpad_id_data *tp) | ||
6148 | { | 6348 | { |
6149 | const struct dmi_device *dev = NULL; | 6349 | const struct dmi_device *dev = NULL; |
6150 | char ec_fw_string[18]; | 6350 | char ec_fw_string[18]; |
6351 | char const *s; | ||
6151 | 6352 | ||
6152 | if (!tp) | 6353 | if (!tp) |
6153 | return; | 6354 | return -EINVAL; |
6154 | 6355 | ||
6155 | memset(tp, 0, sizeof(*tp)); | 6356 | memset(tp, 0, sizeof(*tp)); |
6156 | 6357 | ||
@@ -6159,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
6159 | else if (dmi_name_in_vendors("LENOVO")) | 6360 | else if (dmi_name_in_vendors("LENOVO")) |
6160 | tp->vendor = PCI_VENDOR_ID_LENOVO; | 6361 | tp->vendor = PCI_VENDOR_ID_LENOVO; |
6161 | else | 6362 | else |
6162 | return; | 6363 | return 0; |
6163 | 6364 | ||
6164 | tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), | 6365 | s = dmi_get_system_info(DMI_BIOS_VERSION); |
6165 | GFP_KERNEL); | 6366 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); |
6367 | if (s && !tp->bios_version_str) | ||
6368 | return -ENOMEM; | ||
6166 | if (!tp->bios_version_str) | 6369 | if (!tp->bios_version_str) |
6167 | return; | 6370 | return 0; |
6168 | tp->bios_model = tp->bios_version_str[0] | 6371 | tp->bios_model = tp->bios_version_str[0] |
6169 | | (tp->bios_version_str[1] << 8); | 6372 | | (tp->bios_version_str[1] << 8); |
6170 | 6373 | ||
@@ -6183,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
6183 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; | 6386 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; |
6184 | 6387 | ||
6185 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); | 6388 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
6389 | if (!tp->ec_version_str) | ||
6390 | return -ENOMEM; | ||
6186 | tp->ec_model = ec_fw_string[0] | 6391 | tp->ec_model = ec_fw_string[0] |
6187 | | (ec_fw_string[1] << 8); | 6392 | | (ec_fw_string[1] << 8); |
6188 | break; | 6393 | break; |
6189 | } | 6394 | } |
6190 | } | 6395 | } |
6191 | 6396 | ||
6192 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), | 6397 | s = dmi_get_system_info(DMI_PRODUCT_VERSION); |
6193 | GFP_KERNEL); | 6398 | if (s && !strnicmp(s, "ThinkPad", 8)) { |
6194 | if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { | 6399 | tp->model_str = kstrdup(s, GFP_KERNEL); |
6195 | kfree(tp->model_str); | 6400 | if (!tp->model_str) |
6196 | tp->model_str = NULL; | 6401 | return -ENOMEM; |
6197 | } | 6402 | } |
6198 | 6403 | ||
6199 | tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), | 6404 | s = dmi_get_system_info(DMI_PRODUCT_NAME); |
6200 | GFP_KERNEL); | 6405 | tp->nummodel_str = kstrdup(s, GFP_KERNEL); |
6406 | if (s && !tp->nummodel_str) | ||
6407 | return -ENOMEM; | ||
6408 | |||
6409 | return 0; | ||
6201 | } | 6410 | } |
6202 | 6411 | ||
6203 | static int __init probe_for_thinkpad(void) | 6412 | static int __init probe_for_thinkpad(void) |
@@ -6460,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void) | |||
6460 | 6669 | ||
6461 | /* Driver-level probe */ | 6670 | /* Driver-level probe */ |
6462 | 6671 | ||
6463 | get_thinkpad_model_data(&thinkpad_id); | 6672 | ret = get_thinkpad_model_data(&thinkpad_id); |
6673 | if (ret) { | ||
6674 | printk(TPACPI_ERR | ||
6675 | "unable to get DMI data: %d\n", ret); | ||
6676 | thinkpad_acpi_module_exit(); | ||
6677 | return ret; | ||
6678 | } | ||
6464 | ret = probe_for_thinkpad(); | 6679 | ret = probe_for_thinkpad(); |
6465 | if (ret) { | 6680 | if (ret) { |
6466 | thinkpad_acpi_module_exit(); | 6681 | thinkpad_acpi_module_exit(); |