• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) International Business Machines  Corp., 2001
3  * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
4  *
5  * This program is free software;  you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  * the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program;  if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 /*
21  *  HISTORY:
22  *    06/09/2003 Initial creation mridge@us.ibm.com
23  *      -Ported
24  *  updated - 01/09/2005 Updates from Intel to add functionality
25  *
26  *  01/03/2009 Márton Németh <nm127@freemail.hu>
27  *   - Updated for Linux kernel 2.6.28
28  */
29 
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/init.h>
33 #include <linux/types.h>
34 #include <linux/fs.h>
35 #include <linux/blkdev.h>
36 #include <linux/ioctl.h>
37 #include <linux/pm.h>
38 #include <linux/acpi.h>
39 #include <linux/genhd.h>
40 #include <linux/dmi.h>
41 #include <linux/nls.h>
42 
43 #include "ltp_acpi.h"
44 
45 MODULE_AUTHOR("Martin Ridgeway <mridge@us.ibm.com>");
46 MODULE_AUTHOR("Alexey Kodanev <alexey.kodanev@oracle.com>");
47 MODULE_DESCRIPTION("ACPI LTP Test Driver");
48 MODULE_LICENSE("GPL");
49 ACPI_MODULE_NAME("LTP_ACPI")
50 
51 #define prk_err(fmt, ...) \
52 	pr_err(ACPI_TEST_NAME ": " fmt "\n", ##__VA_ARGS__)
53 #define prk_alert(fmt, ...) \
54 	pr_alert(ACPI_TEST_NAME ": " fmt "\n", ##__VA_ARGS__)
55 #define prk_info(fmt, ...) \
56 	pr_info(ACPI_TEST_NAME ": " fmt "\n", ##__VA_ARGS__)
57 
acpi_failure(acpi_status status,const char * name)58 static int acpi_failure(acpi_status status, const char *name)
59 {
60 	if (ACPI_FAILURE(status)) {
61 		ACPI_EXCEPTION((AE_INFO, status, name));
62 		return 1;
63 	}
64 	return 0;
65 }
66 
67 /* points to the string of the last found object _STR */
68 static char *str_obj_result;
69 
70 /* sysfs device path of the last found device */
71 static char *sysfs_path;
72 
73 /* first found device with _CRS */
74 static acpi_handle res_handle;
75 
get_str_object(acpi_handle handle)76 static acpi_status get_str_object(acpi_handle handle)
77 {
78 	int res;
79 	acpi_status status;
80 	acpi_handle temp = 0;
81 	union acpi_object *str_obj;
82 	char *buf = NULL;
83 
84 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
85 
86 	status = acpi_get_handle(handle, "_STR", &temp);
87 
88 	if (ACPI_SUCCESS(status) &&
89 	    !acpi_evaluate_object(handle, "_STR", NULL, &buffer)) {
90 
91 		str_obj = buffer.pointer;
92 
93 		buf = kmalloc(str_obj->buffer.length / 2, GFP_KERNEL);
94 		if (!buf) {
95 			kfree(str_obj);
96 			return AE_NO_MEMORY;
97 		}
98 
99 		res = utf16s_to_utf8s((wchar_t *)str_obj->buffer.pointer,
100 			str_obj->buffer.length, UTF16_LITTLE_ENDIAN, buf,
101 			str_obj->buffer.length / 2);
102 
103 		buf[res] = '\0';
104 
105 		kfree(str_obj_result);
106 		str_obj_result = buf;
107 		kfree(str_obj);
108 	}
109 
110 	return status;
111 }
112 
get_crs_object(acpi_handle handle)113 static void get_crs_object(acpi_handle handle)
114 {
115 	acpi_status status;
116 	acpi_handle temp;
117 	if (!res_handle) {
118 		status = acpi_get_handle(handle, METHOD_NAME__CRS, &temp);
119 		if (ACPI_SUCCESS(status))
120 			res_handle = handle;
121 	}
122 }
123 
get_sysfs_path(acpi_handle handle)124 static void get_sysfs_path(acpi_handle handle)
125 {
126 	acpi_status status;
127 	struct acpi_device *device;
128 
129 	kfree(sysfs_path);
130 	sysfs_path = NULL;
131 
132 	status = acpi_bus_get_device(handle, &device);
133 	if (ACPI_SUCCESS(status))
134 		sysfs_path = kobject_get_path(&device->dev.kobj, GFP_KERNEL);
135 }
136 
137 /* acpi handle of the last visited device */
138 static acpi_handle start_parent;
139 
acpi_traverse(acpi_handle parent,acpi_handle child)140 static int acpi_traverse(acpi_handle parent, acpi_handle child)
141 {
142 	static char indent[64];
143 	const char * const ind_end = indent + 63;
144 	static const char *ind = ind_end;
145 	acpi_status status;
146 	struct acpi_device_info *dev_info;
147 	acpi_handle new_child;
148 
149 	if (!indent[0])
150 		memset(indent, 0x20, 63);
151 
152 	while (parent) {
153 		status = acpi_get_next_object(ACPI_TYPE_DEVICE,
154 			parent, child, &new_child);
155 
156 		if (ACPI_FAILURE(status)) {
157 			ind += 4;
158 
159 			child = parent;
160 			status = acpi_get_parent(child, &parent);
161 
162 			/* no more devices */
163 			if (ACPI_FAILURE(status)) {
164 				start_parent = 0;
165 				kfree(str_obj_result);
166 				str_obj_result = NULL;
167 				return 0;
168 			}
169 			continue;
170 		}
171 
172 		status = acpi_get_object_info(new_child, &dev_info);
173 		if (acpi_failure(status, "acpi_object_info failed"))
174 			return 1;
175 
176 		get_sysfs_path(new_child);
177 
178 		get_crs_object(new_child);
179 
180 		if (ind < indent)
181 			ind = indent;
182 		else if (ind > ind_end)
183 			ind = ind_end;
184 
185 		/*
186 		 * if we find _STR object we will stop here
187 		 * and save last visited child
188 		 */
189 		if (ACPI_SUCCESS(get_str_object(new_child))) {
190 			prk_info("%s%4.4s: has '_STR' '%s' path '%s'",
191 				ind, (char *)&dev_info->name, str_obj_result,
192 				(sysfs_path) ? sysfs_path : "no path");
193 			ind -= 4;
194 			start_parent = new_child;
195 			kfree(dev_info);
196 			return 0;
197 		}
198 		prk_info("%s%4.4s: path '%s'", ind, (char *)&dev_info->name,
199 			(sysfs_path) ? sysfs_path : "no path");
200 
201 		ind -= 4;
202 		parent = new_child;
203 		child = 0;
204 		kfree(dev_info);
205 	}
206 
207 	return 0;
208 }
209 
acpi_traverse_from_root(void)210 static int acpi_traverse_from_root(void)
211 {
212 	acpi_status status;
213 	struct acpi_device_info *dev_info;
214 	acpi_handle parent = 0, child = 0;
215 
216 	if (!start_parent) {
217 		status = acpi_get_handle(NULL, ACPI_NS_ROOT_PATH, &parent);
218 		if (acpi_failure(status, "acpi_get_handle"))
219 			return 1;
220 		status = acpi_get_object_info(parent, &dev_info);
221 		if (acpi_failure(status, "acpi_object_info failed"))
222 			return 1;
223 		prk_info("start from %4.4s", (char *)&dev_info->name);
224 	} else {
225 		/* continue with the last visited child */
226 		parent = start_parent;
227 	}
228 
229 	return acpi_traverse(parent, child);
230 }
231 
232 /* first found device with _STR */
233 static acpi_handle dev_handle;
234 static int acpi_hw_reduced;
235 
236 /* check if PM2 control register supported */
237 static bool pm2_supported;
238 
acpi_init(void)239 static int acpi_init(void)
240 {
241 	acpi_status status;
242 	acpi_handle parent_handle;
243 	struct acpi_table_fadt *fadt;
244 	struct acpi_table_header *table;
245 	struct acpi_device_info *dev_info;
246 	pm2_supported = true;
247 
248 	status = acpi_get_table(ACPI_SIG_FADT, 0, &table);
249 	if (ACPI_SUCCESS(status)) {
250 		fadt = (struct acpi_table_fadt *)table;
251 		if (fadt->flags & ACPI_FADT_HW_REDUCED)
252 			acpi_hw_reduced = 1;
253 		if (fadt->pm2_control_block == 0 || fadt->pm2_control_length == 0)
254 			pm2_supported = false;
255 	}
256 	if (acpi_hw_reduced)
257 		prk_alert("Detected the Hardware-reduced ACPI mode");
258 
259 	prk_alert("TEST -- acpi_get_handle ");
260 	status = acpi_get_handle(NULL, "\\_SB", &parent_handle);
261 	if (acpi_failure(status, "acpi_get_handle"))
262 		return 1;
263 
264 	/* get first device on SYS bus, it will be used in other tests */
265 	while (acpi_get_next_object(ACPI_TYPE_DEVICE,
266 		parent_handle, 0, &dev_handle) == 0) {
267 		parent_handle = dev_handle;
268 	}
269 
270 	status = acpi_get_object_info(dev_handle, &dev_info);
271 	if (acpi_failure(status, "acpi_object_info failed"))
272 		return 1;
273 
274 	prk_alert("ACPI object name %4.4s, type %d", (char *)&dev_info->name,
275 		dev_info->type);
276 	kfree(dev_info);
277 
278 	prk_alert("TEST -- acpi_get_parent ");
279 	status = acpi_get_parent(dev_handle, &parent_handle);
280 	return acpi_failure(status, "acpi_get_parent failed");
281 }
282 
283 /*
284  * acpi_bus_notify
285  * ---------------
286  * Callback for all 'system-level' device notifications (values 0x00-0x7F).
287  */
acpi_bus_notify(acpi_handle handle,u32 type,void * data)288 static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
289 {
290 	prk_alert("Register ACPI Bus Notify callback function");
291 }
292 
acpi_test_notify_handler(void)293 static int acpi_test_notify_handler(void)
294 {
295 	acpi_status status;
296 
297 	prk_alert("TEST -- acpi_install_notify_handler");
298 
299 	status = acpi_install_notify_handler(dev_handle,
300 		ACPI_SYSTEM_NOTIFY, &acpi_bus_notify, NULL);
301 
302 	if (ACPI_SUCCESS(status)) {
303 		prk_alert("TEST -- acpi_remove_notify_handler");
304 		status = acpi_remove_notify_handler(dev_handle,
305 			ACPI_SYSTEM_NOTIFY, &acpi_bus_notify);
306 		return acpi_failure(status, "acpi_remove_notify_handler");
307 	} else if (status != AE_ALREADY_EXISTS) {
308 		return acpi_failure(status, "acpi_install_notify_handler");
309 	}
310 
311 	return 0;
312 }
313 
ltp_test_power_button_ev_handler(void * context)314 static u32 ltp_test_power_button_ev_handler(void *context)
315 {
316 	prk_alert("ltp_test_power_button_ev_handler");
317 	return 1;
318 }
319 
ltp_test_sleep_button_ev_handler(void * context)320 static u32 ltp_test_sleep_button_ev_handler(void *context)
321 {
322 	prk_alert("ltp_test_sleep_button_ev_handler");
323 	return 1;
324 }
325 
acpi_test_event_handler(void)326 static int acpi_test_event_handler(void)
327 {
328 	int err = 0;
329 	acpi_status status;
330 
331 	prk_alert("TEST -- acpi_install_fixed_event_handler");
332 	if (acpi_hw_reduced) {
333 		prk_alert("Skipped due to the HW-reduced mode");
334 		return 0;
335 	}
336 	status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
337 		ltp_test_power_button_ev_handler, NULL);
338 
339 	if (ACPI_SUCCESS(status)) {
340 		prk_alert("TEST -- acpi_remove_fixed_event_handler");
341 		status = acpi_remove_fixed_event_handler(
342 			ACPI_EVENT_POWER_BUTTON,
343 			ltp_test_power_button_ev_handler);
344 		err = acpi_failure(status, "remove fixed event handler");
345 	} else if (status != AE_ALREADY_EXISTS) {
346 		err = acpi_failure(status, "install fixed event handler");
347 	}
348 
349 	prk_alert("TEST -- acpi_install_fixed_event_handler");
350 	status = acpi_install_fixed_event_handler(ACPI_EVENT_RTC,
351 		ltp_test_sleep_button_ev_handler, NULL);
352 
353 	if (ACPI_SUCCESS(status)) {
354 		prk_alert("TEST -- acpi_remove_fixed_event_handler");
355 		status = acpi_remove_fixed_event_handler(
356 			ACPI_EVENT_RTC,
357 			ltp_test_sleep_button_ev_handler);
358 		err |= acpi_failure(status, "remove fixed event handler");
359 	} else if (status != AE_ALREADY_EXISTS) {
360 		err |= acpi_failure(status, "install fixed event handler");
361 	}
362 
363 	return err;
364 }
365 
366 #ifndef ACPI_EC_UDELAY_GLK
367 #define ACPI_EC_UDELAY_GLK	1000	/* Wait 1ms max. to get global lock */
368 #endif
369 
acpi_global_lock(void)370 static int acpi_global_lock(void)
371 {
372 	acpi_status status;
373 	u32 global_lock = 0;
374 
375 	prk_alert("TEST -- acpi_acquire_global_lock ");
376 	if (acpi_hw_reduced) {
377 		prk_alert("Skipped due to the HW-reduced mode");
378 		return 0;
379 	}
380 	status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &global_lock);
381 	if (acpi_failure(status, "acpi_acquire_global_lock"))
382 		return 1;
383 
384 	prk_alert("TEST -- acpi_release_global_lock ");
385 	status = acpi_release_global_lock(global_lock);
386 	return acpi_failure(status, "acpi_release_global_lock");
387 }
388 
acpi_test_bus(void)389 static int acpi_test_bus(void)
390 {
391 	int state = 0;
392 	acpi_status status;
393 	acpi_handle bus_handle;
394 	struct acpi_device *device;
395 
396 	status = acpi_get_handle(NULL, "\\_SB", &bus_handle);
397 	if (acpi_failure(status, "acpi_get_handle"))
398 		return 1;
399 
400 	prk_alert("TEST -- acpi_bus_get_device");
401 	status = acpi_bus_get_device(bus_handle, &device);
402 	if (acpi_failure(status, "acpi_bus_get_device"))
403 		return 1;
404 
405 	prk_alert("TEST -- acpi_bus_update_power ");
406 	status = acpi_bus_update_power(device->handle, &state);
407 	if (acpi_failure(status, "error reading power state"))
408 		return 1;
409 
410 	prk_info("acpi bus power state is %d", state);
411 	return 0;
412 }
413 
acpi_ec_io_ports(struct acpi_resource * resource,void * context)414 static acpi_status acpi_ec_io_ports(struct acpi_resource *resource,
415 	void *context)
416 {
417 	return 0;
418 }
419 
acpi_test_resources(void)420 static int acpi_test_resources(void)
421 {
422 	int err = 0;
423 	acpi_status status;
424 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
425 
426 	/* skip if we don't find device with _CRC */
427 	if (res_handle == 0)
428 		return 0;
429 
430 	prk_alert("TEST -- acpi_get_current_resources");
431 	status = acpi_get_current_resources(res_handle, &buffer);
432 	err = acpi_failure(status, "failed get_current_resources");
433 
434 #ifdef ACPI_FUTURE_USAGE
435 	prk_alert("TEST -- acpi_get_possible_resources");
436 	status = acpi_get_possible_resources(res_handle, &buffer);
437 	err |= acpi_failure(status, "get_possible_resources");
438 #endif
439 
440 	prk_alert("TEST -- acpi_walk_resources ");
441 	status = acpi_walk_resources(res_handle, METHOD_NAME__CRS,
442 		acpi_ec_io_ports, NULL);
443 	err |= acpi_failure(status, "Failed walk_resources");
444 
445 	return err;
446 }
447 
acpi_sleep_test(void)448 static int acpi_sleep_test(void)
449 {
450 	int err = 0;
451 	acpi_status status;
452 	u32 i;
453 	u8 type_a, type_b;
454 	prk_alert("TEST -- acpi_get_sleep_type_data ");
455 
456 	for (i = 0; i < ACPI_S_STATE_COUNT; ++i) {
457 		status = acpi_get_sleep_type_data(i, &type_a, &type_b);
458 		if (ACPI_SUCCESS(status)) {
459 			prk_info("get_sleep_type_data S%d a:%d b:%d",
460 				i, type_a, type_b);
461 		} else if (status != AE_NOT_FOUND) {
462 			err |= 1;
463 		}
464 	}
465 
466 	return err;
467 }
468 
acpi_test_register(void)469 static int acpi_test_register(void)
470 {
471 	int i, err = 0;
472 	u32 val;
473 	acpi_status status;
474 
475 	prk_alert("TEST -- acpi_read_bit_register");
476 	if (acpi_hw_reduced) {
477 		prk_alert("Skipped due to the HW-reduced mode");
478 		return 0;
479 	}
480 	/*
481 	 * ACPICA: Remove obsolete Flags parameter.
482 	 * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;
483 	 * a=commitdiff;h=d8c71b6d3b21cf21ad775e1cf6da95bf87bd5ad4
484 	 *
485 	 * ACPICA: Rename ACPI bit register access functions
486 	 * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
487 	 * commit/?id=50ffba1bd3120b069617455545bc27bcf3cf7579
488 	 */
489 	for (i = 0; i < ACPI_NUM_BITREG; ++i) {
490 		if (i == ACPI_BITREG_ARB_DISABLE && !pm2_supported)
491 			continue;
492 		status = acpi_read_bit_register(i, &val);
493 		err |= acpi_failure(status, "acpi_read_bit_register");
494 		if (ACPI_SUCCESS(status))
495 			prk_alert("get register: %02x val: %04x", i, val);
496 	}
497 
498 	return err;
499 }
500 
ltp_get_dev_callback(acpi_handle obj,u32 depth,void * context,void ** ret)501 static acpi_status ltp_get_dev_callback(acpi_handle obj, u32 depth,
502 	void *context, void **ret)
503 {
504 	char *name = context;
505 	char fullname[20];
506 
507 	/*
508 	 * Only SBA shows up in ACPI namespace, so its CSR space
509 	 * includes both SBA and IOC.  Make SBA and IOC show up
510 	 * separately in PCI space.
511 	 */
512 	sprintf(fullname, "%s SBA", name);
513 	prk_info("get_dev_callback SBA name %s", fullname);
514 	sprintf(fullname, "%s IOC", name);
515 	prk_info("get_dev_callback IOC name %s", fullname);
516 
517 	return 0;
518 }
519 
acpi_test_dev_callback(void)520 static int acpi_test_dev_callback(void)
521 {
522 	acpi_status status;
523 	prk_alert("TEST -- acpi_get_devices ");
524 	status = acpi_get_devices(NULL, ltp_get_dev_callback, "LTP0001", NULL);
525 	return acpi_failure(status, "acpi_get_devices");
526 }
527 
528 static int current_test_case;
529 static int test_result;
530 
device_release(struct device * dev)531 static void device_release(struct device *dev)
532 {
533 	prk_info("device released");
534 }
535 
536 static struct device tdev = {
537 	.init_name	= ACPI_TEST_NAME,
538 	.release	= device_release,
539 };
540 
541 /* print test result to sysfs file */
sys_result(struct device * dev,struct device_attribute * attr,char * buf)542 static ssize_t sys_result(struct device *dev,
543 	struct device_attribute *attr, char *buf)
544 {
545 	return scnprintf(buf, PAGE_SIZE, "%d\n", test_result);
546 }
547 static DEVICE_ATTR(result, S_IRUSR, sys_result, NULL);
548 
549 /* print found device description */
sys_str(struct device * dev,struct device_attribute * attr,char * buf)550 static ssize_t sys_str(struct device *dev,
551 	struct device_attribute *attr, char *buf)
552 {
553 	if (str_obj_result)
554 		return scnprintf(buf, PAGE_SIZE, "%s", str_obj_result);
555 	else
556 		return 0;
557 }
558 static DEVICE_ATTR(str, S_IRUSR, sys_str, NULL);
559 
560 /* print found device's sysfs path */
sys_path(struct device * dev,struct device_attribute * attr,char * buf)561 static ssize_t sys_path(struct device *dev,
562 	struct device_attribute *attr, char *buf)
563 {
564 	if (sysfs_path)
565 		return scnprintf(buf, PAGE_SIZE, "%s", sysfs_path);
566 	else
567 		return 0;
568 }
569 static DEVICE_ATTR(path, S_IRUSR, sys_path, NULL);
570 
sys_acpi_disabled(struct device * dev,struct device_attribute * attr,char * buf)571 static ssize_t sys_acpi_disabled(struct device *dev,
572 				    struct device_attribute *attr, char *buf)
573 {
574 	return scnprintf(buf, PAGE_SIZE, "%d", acpi_disabled);
575 }
576 static DEVICE_ATTR(acpi_disabled, S_IRUSR, sys_acpi_disabled, NULL);
577 
sys_tcase(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)578 static ssize_t sys_tcase(struct device *dev,
579 	struct device_attribute *attr,  const char *buf, size_t count)
580 {
581 	sscanf(buf, "%d", &current_test_case);
582 	prk_info("test-case %d", current_test_case);
583 
584 	switch (current_test_case) {
585 	case ACPI_INIT:
586 		test_result = acpi_init();
587 	break;
588 	case ACPI_TRAVERSE:
589 		test_result = acpi_traverse_from_root();
590 	break;
591 	case ACPI_NOTIFY_HANDLER:
592 		test_result = acpi_test_notify_handler();
593 	break;
594 	case ACPI_EVENT_HANDLER:
595 		test_result = acpi_test_event_handler();
596 	break;
597 	case ACPI_GLOBAL_LOCK:
598 		test_result = acpi_global_lock();
599 	break;
600 	case ACPI_TEST_BUS:
601 		test_result = acpi_test_bus();
602 	break;
603 	case ACPI_TEST_RESOURCES:
604 		test_result = acpi_test_resources();
605 	break;
606 	case ACPI_SLEEP_TEST:
607 		test_result = acpi_sleep_test();
608 	break;
609 	case ACPI_TEST_REGISTER:
610 		test_result = acpi_test_register();
611 	break;
612 	case ACPI_TEST_DEV_CALLBACK:
613 		test_result = acpi_test_dev_callback();
614 	break;
615 	}
616 
617 	return count;
618 }
619 static DEVICE_ATTR(tcase, S_IWUSR, NULL, sys_tcase);
620 
init_module(void)621 int init_module(void)
622 {
623 	int err = 0;
624 	prk_info("Starting module");
625 
626 	err = device_register(&tdev);
627 	if (err) {
628 		prk_err("Unable to register device");
629 		goto err0;
630 	}
631 	prk_info("device registered");
632 
633 	err = device_create_file(&tdev, &dev_attr_result);
634 	if (err) {
635 		prk_err("Can't create sysfs file 'result'");
636 		goto err1;
637 	}
638 
639 	err = device_create_file(&tdev, &dev_attr_str);
640 	if (err) {
641 		prk_err("Can't create sysfs file 'str'");
642 		goto err2;
643 	}
644 
645 	err = device_create_file(&tdev, &dev_attr_tcase);
646 	if (err) {
647 		prk_err(": Can't create sysfs file 'tc'");
648 		goto err3;
649 	}
650 
651 	err = device_create_file(&tdev, &dev_attr_path);
652 	if (err) {
653 		prk_err(": Can't create sysfs file 'path'");
654 		goto err4;
655 	}
656 
657 	err = device_create_file(&tdev, &dev_attr_acpi_disabled);
658 	if (err) {
659 		prk_err("Can't create sysfs file 'acpi_disabled'");
660 		goto err5;
661 	}
662 
663 	return 0;
664 
665 err5:
666 	device_remove_file(&tdev, &dev_attr_path);
667 err4:
668 	device_remove_file(&tdev, &dev_attr_tcase);
669 err3:
670 	device_remove_file(&tdev, &dev_attr_str);
671 err2:
672 	device_remove_file(&tdev, &dev_attr_result);
673 err1:
674 	device_unregister(&tdev);
675 err0:
676 	return err;
677 }
678 
cleanup_module(void)679 void cleanup_module(void)
680 {
681 	prk_info("Unloading module\n");
682 
683 	kfree(str_obj_result);
684 	kfree(sysfs_path);
685 
686 	device_remove_file(&tdev, &dev_attr_result);
687 	device_remove_file(&tdev, &dev_attr_str);
688 	device_remove_file(&tdev, &dev_attr_tcase);
689 	device_remove_file(&tdev, &dev_attr_path);
690 	device_unregister(&tdev);
691 }
692