]>
Commit | Line | Data |
---|---|---|
9827886d TR |
1 | /* |
2 | * ec_sys.c | |
3 | * | |
4 | * Copyright (C) 2010 SUSE Products GmbH/Novell | |
5 | * Author: | |
6 | * Thomas Renninger <trenn@suse.de> | |
7 | * | |
8 | * This work is licensed under the terms of the GNU GPL, version 2. | |
9 | */ | |
10 | ||
1195a098 TR |
11 | #include <linux/kernel.h> |
12 | #include <linux/acpi.h> | |
13 | #include <linux/debugfs.h> | |
14 | #include "internal.h" | |
15 | ||
16 | MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); | |
17 | MODULE_DESCRIPTION("ACPI EC sysfs access driver"); | |
18 | MODULE_LICENSE("GPL"); | |
19 | ||
9827886d TR |
20 | #define EC_SPACE_SIZE 256 |
21 | ||
1195a098 TR |
22 | struct sysdev_class acpi_ec_sysdev_class = { |
23 | .name = "ec", | |
24 | }; | |
25 | ||
26 | static struct dentry *acpi_ec_debugfs_dir; | |
27 | ||
9827886d TR |
28 | static int acpi_ec_open_io(struct inode *i, struct file *f) |
29 | { | |
30 | f->private_data = i->i_private; | |
31 | return 0; | |
32 | } | |
33 | ||
34 | static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, | |
35 | size_t count, loff_t *off) | |
36 | { | |
37 | /* Use this if support reading/writing multiple ECs exists in ec.c: | |
38 | * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; | |
39 | */ | |
40 | unsigned int size = EC_SPACE_SIZE; | |
41 | u8 *data = (u8 *) buf; | |
42 | loff_t init_off = *off; | |
43 | int err = 0; | |
44 | ||
45 | if (*off >= size) | |
46 | return 0; | |
47 | if (*off + count >= size) { | |
48 | size -= *off; | |
49 | count = size; | |
50 | } else | |
51 | size = count; | |
52 | ||
53 | while (size) { | |
54 | err = ec_read(*off, &data[*off - init_off]); | |
55 | if (err) | |
56 | return err; | |
57 | *off += 1; | |
58 | size--; | |
59 | } | |
60 | return count; | |
61 | } | |
62 | ||
63 | static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, | |
64 | size_t count, loff_t *off) | |
65 | { | |
66 | /* Use this if support reading/writing multiple ECs exists in ec.c: | |
67 | * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; | |
68 | */ | |
69 | ||
70 | unsigned int size = count; | |
71 | loff_t init_off = *off; | |
72 | u8 *data = (u8 *) buf; | |
73 | int err = 0; | |
74 | ||
75 | if (*off >= EC_SPACE_SIZE) | |
76 | return 0; | |
77 | if (*off + count >= EC_SPACE_SIZE) { | |
78 | size = EC_SPACE_SIZE - *off; | |
79 | count = size; | |
80 | } | |
81 | ||
82 | while (size) { | |
83 | u8 byte_write = data[*off - init_off]; | |
84 | err = ec_write(*off, byte_write); | |
85 | if (err) | |
86 | return err; | |
87 | ||
88 | *off += 1; | |
89 | size--; | |
90 | } | |
91 | return count; | |
92 | } | |
93 | ||
94 | static struct file_operations acpi_ec_io_ops = { | |
95 | .owner = THIS_MODULE, | |
96 | .open = acpi_ec_open_io, | |
97 | .read = acpi_ec_read_io, | |
98 | .write = acpi_ec_write_io, | |
99 | }; | |
100 | ||
1195a098 TR |
101 | int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) |
102 | { | |
103 | struct dentry *dev_dir; | |
104 | char name[64]; | |
105 | if (ec_device_count == 0) { | |
106 | acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL); | |
107 | if (!acpi_ec_debugfs_dir) | |
108 | return -ENOMEM; | |
109 | } | |
110 | ||
111 | sprintf(name, "ec%u", ec_device_count); | |
112 | dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir); | |
113 | if (!dev_dir) { | |
114 | if (ec_device_count == 0) | |
115 | debugfs_remove_recursive(acpi_ec_debugfs_dir); | |
116 | /* TBD: Proper cleanup for multiple ECs */ | |
117 | return -ENOMEM; | |
118 | } | |
119 | ||
120 | debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe); | |
121 | debugfs_create_bool("use_global_lock", 0444, dev_dir, | |
122 | (u32 *)&first_ec->global_lock); | |
9827886d | 123 | debugfs_create_file("io", 0666, dev_dir, ec, &acpi_ec_io_ops); |
1195a098 TR |
124 | return 0; |
125 | } | |
126 | ||
127 | static int __init acpi_ec_sys_init(void) | |
128 | { | |
129 | int err = 0; | |
130 | if (first_ec) | |
131 | err = acpi_ec_add_debugfs(first_ec, 0); | |
132 | else | |
133 | err = -ENODEV; | |
134 | return err; | |
135 | } | |
136 | ||
137 | static void __exit acpi_ec_sys_exit(void) | |
138 | { | |
139 | debugfs_remove_recursive(acpi_ec_debugfs_dir); | |
140 | } | |
141 | ||
142 | module_init(acpi_ec_sys_init); | |
143 | module_exit(acpi_ec_sys_exit); |