CHROMIUM: ACPI update to support the latest firmware.
[cascardo/linux.git] / drivers / platform / x86 / chromeos_acpi.c
1 /*
2  *  chromeos_acpi.c - ChromeOS specific ACPI support
3  *
4  *
5  *  Copyright (C) 2010 ChromeOS contributors
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 as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
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.
16  *
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
20  *
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).
24  *
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.
28  *
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.
32  */
33
34 #include <linux/kernel.h>
35 #include <linux/module.h>
36 #include <linux/platform_device.h>
37 #include <linux/acpi.h>
38
39 MODULE_AUTHOR("Google Inc.");
40 MODULE_DESCRIPTION("Chrome OS Extras Driver");
41 MODULE_LICENSE("GPL");
42
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"
48
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 */
52         {"", 0},
53 };
54
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);
58
59 static struct acpi_driver chromeos_acpi_driver = {
60         .name = "ChromeOS Device",
61         .class = "ChromeOS",
62         .ids = chromeos_device_ids,
63         .ops = {
64                 .add = chromeos_device_add,
65                 .remove = chromeos_device_remove,
66                 },
67         .owner = THIS_MODULE,
68 };
69
70 /* The methods the chromeos ACPI device is supposed to export */
71 static char *chromeos_methods[] = {
72         "CHSW", "HWID", "BINF", "GPIO", "CHNV", "FWID"
73 };
74
75 /*
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
81  */
82 struct acpi_attribute {
83         struct device_attribute dev_attr;
84         struct acpi_attribute *next_acpi_attr;
85         char *value;
86 };
87
88 /*
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.
92  */
93 struct acpi_attribute_group {
94         struct attribute_group ag;
95         struct acpi_attribute_group *next_acpi_attr_group;
96 };
97
98 /*
99  * ChromeOS ACPI device wrapper adds links pointing at lists of allocated
100  * attributes and attribute groups.
101  */
102 struct chromeos_acpi_dev {
103         struct platform_device *p_dev;
104         struct acpi_attribute *attributes;
105         struct acpi_attribute_group *groups;
106 };
107
108 static struct chromeos_acpi_dev chromeos_acpi = { };
109
110 /*
111  * To show attribute value just access the container structure's `value'
112  * field.
113  */
114 static ssize_t show_acpi_attribute(struct device *dev,
115                                    struct device_attribute *attr, char *buf)
116 {
117         struct acpi_attribute *paa;
118
119         paa = container_of(attr, struct acpi_attribute, dev_attr);
120         return snprintf(buf, PAGE_SIZE, paa->value);
121 }
122
123 /*
124  * create_sysfs_attribute() create and initialize an ACPI sys fs attribute
125  *                          structure.
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
130  *
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.
136  *
137  * Returns: a pointer to the allocated and initialized structure, or null if
138  * allocation failed.
139  *
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
146  * creation failure.
147  */
148 static struct acpi_attribute *create_sysfs_attribute(char *value, char *name,
149                                                      int count, int instance)
150 {
151         struct acpi_attribute *paa;
152         int total_size, room_left;
153         int value_len = strlen(value);
154
155         if (!value_len) {
156                 return NULL;
157         }
158
159         value_len++; /* include the terminating zero */
160
161         /*
162          * total allocation size includes (all strings with including
163          * terminating zeros):
164          *
165          * - value string
166          * - attribute structure size
167          * - name string
168          * - suffix string (in case there are multiple instances)
169          * - dot separating the instance suffix
170          */
171
172         total_size = value_len + sizeof(struct acpi_attribute) +
173                         strlen(name) + 1;
174
175         if (count != 1) {
176                 if (count >= 1000) {
177                         printk(MY_ERR "%s: too many (%d) instances of %s\n",
178                                __FUNCTION__, count, name);
179                         return;
180                 }
181                 /* allow up to three digits and the dot */
182                 total_size += 4;
183         }
184
185         paa = kzalloc(total_size, GFP_KERNEL);
186         if (!paa) {
187                 printk(MY_ERR "out of memory in %s!\n", __FUNCTION__);
188                 return NULL;
189         }
190
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;
197
198         room_left = total_size - value_len -
199                         offsetof(struct acpi_attribute, value);
200
201         if (count == 1) {
202                 snprintf((char *)paa->dev_attr.attr.name, room_left, name);
203         } else {
204                 snprintf((char *)paa->dev_attr.attr.name, room_left,
205                          "%s.%d", name, instance);
206         }
207
208         paa->next_acpi_attr = chromeos_acpi.attributes;
209         chromeos_acpi.attributes = paa;
210
211         return paa;
212 }
213
214 /*
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
221  */
222
223 static void add_sysfs_attribute(char *value, char *name,
224                                 int count, int instance)
225 {
226         struct acpi_attribute *paa =
227             create_sysfs_attribute(value, name, count, instance);
228
229         if (!paa) {
230                 return;
231         }
232
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);
235         }
236 }
237
238 /*
239  * handle_nested_acpi_package() create sysfs group including attributes
240  *                              representing a nested ACPI package.
241  *
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
246  *
247  * The created group is called @pm in case there is a single instance, or
248  * @pm.@instance otherwise.
249  *
250  * All group and attribute storage allocations are included in the lists for
251  * tracking of allocated memory.
252  */
253 static void handle_nested_acpi_package(union acpi_object *po, char *pm,
254                                        int total, int instance)
255 {
256         int ii, size, count, jj;
257         struct acpi_attribute_group *aag;
258
259         count = po->package.count;
260
261         size = strlen(pm) + 1 + sizeof(struct acpi_attribute_group) +
262             sizeof(struct attribute *) * (count + 1);
263
264         if (total != 1) {
265                 if (total >= 1000) {
266                         printk(MY_ERR "%s: too many (%d) instances of %s\n",
267                                __FUNCTION__, total, pm);
268                         return;
269                 }
270                 /* allow up to three digits and the dot */
271                 size += 4;
272         }
273
274         aag = kzalloc(size, GFP_KERNEL);
275         if (!aag) {
276                 printk(MY_ERR "out of memory in %s!\n", __FUNCTION__);
277                 return;
278         }
279
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];
284
285         /* room left in the buffer */
286         size = size - (aag->ag.name - (char *)aag);
287
288         if (total != 1) {
289                 snprintf((char *)aag->ag.name, size, "%s.%d", pm, instance);
290         } else {
291                 snprintf((char *)aag->ag.name, size, "%s", pm);
292         }
293
294         jj = 0;                 /* attribute index */
295         for (ii = 0; ii < count; ii++) {
296                 union acpi_object *element = po->package.elements + ii;
297                 int copy_size = 0;
298                 char attr_value[40];    /* 40 chars be enough for names */
299                 struct acpi_attribute *paa;
300
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);
306                         break;
307
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);
314                         break;
315
316                 default:
317                         printk(MY_ERR "ignoring nested type %d\n",
318                                element->type);
319                         continue;
320                 }
321                 aag->ag.attrs[jj++] = &paa->dev_attr.attr;
322         }
323
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);
326         }
327 }
328
329 /*
330  * handle_acpi_package() create sysfs group including attributes
331  *                       representing an ACPI package.
332  *
333  * @po: package contents as returned by ACPI
334  * @pm: name of the group
335  *
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
338  * package.
339  */
340 static void handle_acpi_package(union acpi_object *po, char *pm)
341 {
342         int jj;
343         int count = po->package.count;
344         for (jj = 0; jj < count; jj++) {
345                 union acpi_object *element = po->package.elements + jj;
346                 int copy_size = 0;
347                 char attr_value[40];    /* 40 chars be enough for names */
348
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);
354                         break;
355
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);
362                         break;
363
364                 case ACPI_TYPE_PACKAGE:
365                         handle_nested_acpi_package(element, pm, count, jj);
366                         break;
367
368                 default:
369                         printk(MY_ERR "ignoring type %d\n", element->type);
370                         break;
371                 }
372         }
373 }
374
375 static int chromeos_device_add(struct acpi_device *device)
376 {
377         int ii;
378
379         for (ii = 0; ii < ARRAY_SIZE(chromeos_methods); ii++) {
380                 union acpi_object *po;
381                 acpi_status status;
382                 struct acpi_buffer output;
383                 char *pm = chromeos_methods[ii];
384
385                 output.length = ACPI_ALLOCATE_BUFFER;
386                 output.pointer = NULL;
387
388                 status = acpi_evaluate_object(device->handle,
389                                               pm, NULL, &output);
390
391                 if (!ACPI_SUCCESS(status)) {
392                         printk(MY_ERR "failed to retrieve %s (%d)\n", pm,
393                                status);
394                         continue;
395                 }
396
397                 po = output.pointer;
398
399                 if (po->type != ACPI_TYPE_PACKAGE) {
400                         printk(MY_ERR "%s is not a package, ignored\n", pm);
401                 } else {
402                         handle_acpi_package(po, pm);
403                 }
404                 kfree(po);
405         }
406         return 0;
407 }
408
409 static int chromeos_device_remove(struct acpi_device *device, int type)
410 {
411         return 0;
412 }
413
414 static void chromeos_acpi_exit(void)
415 {
416         acpi_bus_unregister_driver(&chromeos_acpi_driver);
417
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);
423                 kfree(aag);
424         }
425
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);
430                 kfree(aa);
431         }
432
433         platform_device_unregister(chromeos_acpi.p_dev);
434         printk(MY_INFO "removed\n");
435 }
436
437 static int __init chromeos_acpi_init(void)
438 {
439         int ret = 0;
440
441         if (acpi_disabled)
442                 return -ENODEV;
443
444         printk(MY_INFO "ChromeOS ACPI Extras version %s built on %s@%s\n",
445                CHROMEOS_ACPI_VERSION, __DATE__, __TIME__);
446
447         chromeos_acpi.p_dev = platform_device_register_simple("chromeos_acpi",
448                                                               -1, NULL, 0);
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);
452         }
453
454         ret = acpi_bus_register_driver(&chromeos_acpi_driver);
455         if (ret < 0) {
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;
459                 return ret;
460         }
461
462         printk(MY_INFO "installed\n");
463         return 0;
464 }
465
466 module_init(chromeos_acpi_init);
467 module_exit(chromeos_acpi_exit);