2 * chromeos_acpi.c - ChromeOS specific ACPI support
5 * Copyright (C) 2010 ChromeOS contributors
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * This driver attaches to the ChromeOS ACPI device and the exports the values
22 * reported by the ACPI in a sysfs directory
23 * (/sys/devices/platform/chromeos_acpi).
25 * The first version of the driver provides only static information; the
26 * values reported by the driver are the snapshot reported by the ACPI at
27 * driver installation time.
29 * All values are presented in the string form (numbers as decimal values) and
30 * can be accessed as the contents of the appropriate read only files in the
31 * sysfs directory tree originating in /sys/devices/platform/chromeos_acpi.
34 #include <linux/kernel.h>
35 #include <linux/module.h>
36 #include <linux/platform_device.h>
37 #include <linux/acpi.h>
39 MODULE_AUTHOR("Google Inc.");
40 MODULE_DESCRIPTION("Chrome OS Extras Driver");
41 MODULE_LICENSE("GPL");
43 #define MY_LOGPREFIX "chromeos_acpi: "
44 #define MY_ERR KERN_ERR MY_LOGPREFIX
45 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
46 #define MY_INFO KERN_INFO MY_LOGPREFIX
47 #define CHROMEOS_ACPI_VERSION "0.02"
49 static const struct acpi_device_id chromeos_device_ids[] = {
50 {"GGL0001", 0}, /* Google's own */
51 {"PNP6666", 0}, /* dummy name to get us going */
55 MODULE_DEVICE_TABLE(acpi, chromeos_device_ids);
56 static int chromeos_device_add(struct acpi_device *device);
57 static int chromeos_device_remove(struct acpi_device *device, int type);
59 static struct acpi_driver chromeos_acpi_driver = {
60 .name = "ChromeOS Device",
62 .ids = chromeos_device_ids,
64 .add = chromeos_device_add,
65 .remove = chromeos_device_remove,
70 /* The methods the chromeos ACPI device is supposed to export */
71 static char *chromeos_methods[] = {
72 "CHSW", "HWID", "BINF", "GPIO", "CHNV", "FWID"
76 * Representation of a single sys fs attribute. In addition to the standard
77 * device_attribute structure has a link field, allowing to create a list of
78 * these structures (to keep track for de-allocation when removing the driver)
79 * and a pointer to the actual attribute value, reported when accessing the
80 * appropriate sys fs file
82 struct acpi_attribute {
83 struct device_attribute dev_attr;
84 struct acpi_attribute *next_acpi_attr;
89 * Representation of a sys fs attribute group (a sub directory in the device's
90 * sys fs directory). In addition to the standard structure has a link to
91 * allow to keep track of the allocated structures.
93 struct acpi_attribute_group {
94 struct attribute_group ag;
95 struct acpi_attribute_group *next_acpi_attr_group;
99 * ChromeOS ACPI device wrapper adds links pointing at lists of allocated
100 * attributes and attribute groups.
102 struct chromeos_acpi_dev {
103 struct platform_device *p_dev;
104 struct acpi_attribute *attributes;
105 struct acpi_attribute_group *groups;
108 static struct chromeos_acpi_dev chromeos_acpi = { };
111 * To show attribute value just access the container structure's `value'
114 static ssize_t show_acpi_attribute(struct device *dev,
115 struct device_attribute *attr, char *buf)
117 struct acpi_attribute *paa;
119 paa = container_of(attr, struct acpi_attribute, dev_attr);
120 return snprintf(buf, PAGE_SIZE, paa->value);
124 * create_sysfs_attribute() create and initialize an ACPI sys fs attribute
126 * @value: attribute value
127 * @name: base attribute name
128 * @count: total number of instances of this attribute
129 * @instance: instance number of this particular attribute
131 * This function allocates and initializes the structure containing all
132 * information necessary to add a sys fs attribute. In case the attribute has
133 * just a single instance, the attribute file name is equal to the @name
134 * parameter . In case the attribute has several instances, the attribute
135 * file name is @name.@instance.
137 * Returns: a pointer to the allocated and initialized structure, or null if
140 * As a side effect, the allocated structure is added to the list in the
141 * chromeos_acpi structure. Note that the actual attribute creation is not
142 * attempted yet, in case of creation error the structure would not have an
143 * actual attribute associated with it, so when de-installing the driver this
144 * structure would be used to try to remove an attribute which does not exist.
145 * This is considered acceptable, as there is no reason for sys fs attribute
148 static struct acpi_attribute *create_sysfs_attribute(char *value, char *name,
149 int count, int instance)
151 struct acpi_attribute *paa;
152 int total_size, room_left;
153 int value_len = strlen(value);
159 value_len++; /* include the terminating zero */
162 * total allocation size includes (all strings with including
163 * terminating zeros):
166 * - attribute structure size
168 * - suffix string (in case there are multiple instances)
169 * - dot separating the instance suffix
172 total_size = value_len + sizeof(struct acpi_attribute) +
177 printk(MY_ERR "%s: too many (%d) instances of %s\n",
178 __FUNCTION__, count, name);
181 /* allow up to three digits and the dot */
185 paa = kzalloc(total_size, GFP_KERNEL);
187 printk(MY_ERR "out of memory in %s!\n", __FUNCTION__);
191 paa->dev_attr.attr.owner = THIS_MODULE;
192 paa->dev_attr.attr.mode = 0444; /* read only */
193 paa->dev_attr.show = show_acpi_attribute;
194 paa->value = (char *)(paa + 1);
195 strcpy(paa->value, value);
196 paa->dev_attr.attr.name = paa->value + value_len;
198 room_left = total_size - value_len -
199 offsetof(struct acpi_attribute, value);
202 snprintf((char *)paa->dev_attr.attr.name, room_left, name);
204 snprintf((char *)paa->dev_attr.attr.name, room_left,
205 "%s.%d", name, instance);
208 paa->next_acpi_attr = chromeos_acpi.attributes;
209 chromeos_acpi.attributes = paa;
215 * add_sysfs_attribute() create and initialize an ACPI sys fs attribute
216 * structure and create the attribute.
217 * @value: attribute value
218 * @name: base attribute name
219 * @count: total number of instances of this attribute
220 * @instance: instance number of this particular attribute
223 static void add_sysfs_attribute(char *value, char *name,
224 int count, int instance)
226 struct acpi_attribute *paa =
227 create_sysfs_attribute(value, name, count, instance);
233 if (device_create_file(&chromeos_acpi.p_dev->dev, &paa->dev_attr)) {
234 printk(MY_ERR "failed to create attribute for %s\n", name);
239 * handle_nested_acpi_package() create sysfs group including attributes
240 * representing a nested ACPI package.
242 * @po: package contents as returned by ACPI
243 * @pm: name of the group
244 * @total: number of instances of this package
245 * @instance: instance number of this particular group
247 * The created group is called @pm in case there is a single instance, or
248 * @pm.@instance otherwise.
250 * All group and attribute storage allocations are included in the lists for
251 * tracking of allocated memory.
253 static void handle_nested_acpi_package(union acpi_object *po, char *pm,
254 int total, int instance)
256 int ii, size, count, jj;
257 struct acpi_attribute_group *aag;
259 count = po->package.count;
261 size = strlen(pm) + 1 + sizeof(struct acpi_attribute_group) +
262 sizeof(struct attribute *) * (count + 1);
266 printk(MY_ERR "%s: too many (%d) instances of %s\n",
267 __FUNCTION__, total, pm);
270 /* allow up to three digits and the dot */
274 aag = kzalloc(size, GFP_KERNEL);
276 printk(MY_ERR "out of memory in %s!\n", __FUNCTION__);
280 aag->next_acpi_attr_group = chromeos_acpi.groups;
281 chromeos_acpi.groups = aag->next_acpi_attr_group;
282 aag->ag.attrs = (struct attribute **)(aag + 1);
283 aag->ag.name = (const char *)&aag->ag.attrs[count + 1];
285 /* room left in the buffer */
286 size = size - (aag->ag.name - (char *)aag);
289 snprintf((char *)aag->ag.name, size, "%s.%d", pm, instance);
291 snprintf((char *)aag->ag.name, size, "%s", pm);
294 jj = 0; /* attribute index */
295 for (ii = 0; ii < count; ii++) {
296 union acpi_object *element = po->package.elements + ii;
298 char attr_value[40]; /* 40 chars be enough for names */
299 struct acpi_attribute *paa;
301 switch (element->type) {
302 case ACPI_TYPE_INTEGER:
303 copy_size = snprintf(attr_value, sizeof(attr_value),
304 "%d", (int)element->integer.value);
305 paa = create_sysfs_attribute(attr_value, pm, count, ii);
308 case ACPI_TYPE_STRING:
309 copy_size = min(element->string.length,
310 sizeof(attr_value) - 1);
311 memcpy(attr_value, element->string.pointer, copy_size);
312 attr_value[copy_size] = '\0';
313 paa = create_sysfs_attribute(attr_value, pm, count, ii);
317 printk(MY_ERR "ignoring nested type %d\n",
321 aag->ag.attrs[jj++] = &paa->dev_attr.attr;
324 if (sysfs_create_group(&chromeos_acpi.p_dev->dev.kobj, &aag->ag)) {
325 printk(MY_ERR "failed to create group %s.%d\n", pm, instance);
330 * handle_acpi_package() create sysfs group including attributes
331 * representing an ACPI package.
333 * @po: package contents as returned by ACPI
334 * @pm: name of the group
336 * Scalar objects included in the package get sys fs attributes created for
337 * them. Nested packages are passed to a function creating a sys fs group per
340 static void handle_acpi_package(union acpi_object *po, char *pm)
343 int count = po->package.count;
344 for (jj = 0; jj < count; jj++) {
345 union acpi_object *element = po->package.elements + jj;
347 char attr_value[40]; /* 40 chars be enough for names */
349 switch (element->type) {
350 case ACPI_TYPE_INTEGER:
351 copy_size = snprintf(attr_value, sizeof(attr_value),
352 "%d", (int)element->integer.value);
353 add_sysfs_attribute(attr_value, pm, count, jj);
356 case ACPI_TYPE_STRING:
357 copy_size = min(element->string.length,
358 sizeof(attr_value) - 1);
359 memcpy(attr_value, element->string.pointer, copy_size);
360 attr_value[copy_size] = '\0';
361 add_sysfs_attribute(attr_value, pm, count, jj);
364 case ACPI_TYPE_PACKAGE:
365 handle_nested_acpi_package(element, pm, count, jj);
369 printk(MY_ERR "ignoring type %d\n", element->type);
375 static int chromeos_device_add(struct acpi_device *device)
379 for (ii = 0; ii < ARRAY_SIZE(chromeos_methods); ii++) {
380 union acpi_object *po;
382 struct acpi_buffer output;
383 char *pm = chromeos_methods[ii];
385 output.length = ACPI_ALLOCATE_BUFFER;
386 output.pointer = NULL;
388 status = acpi_evaluate_object(device->handle,
391 if (!ACPI_SUCCESS(status)) {
392 printk(MY_ERR "failed to retrieve %s (%d)\n", pm,
399 if (po->type != ACPI_TYPE_PACKAGE) {
400 printk(MY_ERR "%s is not a package, ignored\n", pm);
402 handle_acpi_package(po, pm);
409 static int chromeos_device_remove(struct acpi_device *device, int type)
414 static void chromeos_acpi_exit(void)
416 acpi_bus_unregister_driver(&chromeos_acpi_driver);
418 while (chromeos_acpi.groups) {
419 struct acpi_attribute_group *aag;
420 aag = chromeos_acpi.groups;
421 chromeos_acpi.groups = aag->next_acpi_attr_group;
422 sysfs_remove_group(&chromeos_acpi.p_dev->dev.kobj, &aag->ag);
426 while (chromeos_acpi.attributes) {
427 struct acpi_attribute *aa = chromeos_acpi.attributes;
428 chromeos_acpi.attributes = aa->next_acpi_attr;
429 device_remove_file(&chromeos_acpi.p_dev->dev, &aa->dev_attr);
433 platform_device_unregister(chromeos_acpi.p_dev);
434 printk(MY_INFO "removed\n");
437 static int __init chromeos_acpi_init(void)
444 printk(MY_INFO "ChromeOS ACPI Extras version %s built on %s@%s\n",
445 CHROMEOS_ACPI_VERSION, __DATE__, __TIME__);
447 chromeos_acpi.p_dev = platform_device_register_simple("chromeos_acpi",
449 if (IS_ERR(chromeos_acpi.p_dev)) {
450 printk(MY_ERR "unable to register platform device\n");
451 return PTR_ERR(chromeos_acpi.p_dev);
454 ret = acpi_bus_register_driver(&chromeos_acpi_driver);
456 printk(MY_ERR "failed to register driver (%d)\n", ret);
457 platform_device_unregister(chromeos_acpi.p_dev);
458 chromeos_acpi.p_dev = NULL;
462 printk(MY_INFO "installed\n");
466 module_init(chromeos_acpi_init);
467 module_exit(chromeos_acpi_exit);