backlight: Jump to cleanup code if synthesizing backlight string fails
[profile/ivi/weston.git] / src / libbacklight.c
1 /*
2  * libbacklight - userspace interface to Linux backlight control
3  *
4  * Copyright © 2012 Intel Corporation
5  * Copyright 2010 Red Hat <mjg@redhat.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  *
27  * Authors:
28  *    Matthew Garrett <mjg@redhat.com>
29  *    Tiago Vignatti <vignatti@freedesktop.org>
30  */
31
32 #define _GNU_SOURCE
33
34 #include "libbacklight.h"
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <linux/types.h>
39 #include <dirent.h>
40 #include <drm.h>
41 #include <fcntl.h>
42 #include <malloc.h>
43 #include <string.h>
44 #include <errno.h>
45
46 static long backlight_get(struct backlight *backlight, char *node)
47 {
48         char buffer[100];
49         char *path;
50         int fd;
51         long value, ret;
52
53         if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
54                 return -ENOMEM
55 ;
56         fd = open(path, O_RDONLY);
57         if (fd < 0) {
58                 ret = -1;
59                 goto out;
60         }
61
62         ret = read(fd, &buffer, sizeof(buffer));
63         if (ret < 1) {
64                 ret = -1;
65                 goto out;
66         }
67
68         value = strtol(buffer, NULL, 10);
69         ret = value;
70 out:
71         close(fd);
72         if (path)
73                 free(path);
74         return ret;
75 }
76
77 long backlight_get_brightness(struct backlight *backlight)
78 {
79         return backlight_get(backlight, "brightness");
80 }
81
82 long backlight_get_max_brightness(struct backlight *backlight)
83 {
84         return backlight_get(backlight, "max_brightness");
85 }
86
87 long backlight_get_actual_brightness(struct backlight *backlight)
88 {
89         return backlight_get(backlight, "actual_brightness");
90 }
91
92 long backlight_set_brightness(struct backlight *backlight, long brightness)
93 {
94         char *path;
95         char *buffer = NULL;
96         int fd;
97         long ret;
98
99         if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
100                 return -ENOMEM;
101
102         fd = open(path, O_RDWR);
103         if (fd < 0) {
104                 ret = -1;
105                 goto out;
106         }
107
108         ret = read(fd, &buffer, sizeof(buffer));
109         if (ret < 1) {
110                 ret = -1;
111                 goto out;
112         }
113
114         if (asprintf(&buffer, "%ld", brightness) < 0) {
115                 ret = -1;
116                 goto out;
117         }
118
119         ret = write(fd, buffer, strlen(buffer));
120         if (ret < 0) {
121                 ret = -1;
122                 goto out;
123         }
124
125         ret = backlight_get_brightness(backlight);
126         backlight->brightness = ret;
127 out:
128         if (buffer)
129                 free(buffer);
130         if (path)
131                 free(path);
132         close(fd);
133         return ret;
134 }
135
136 void backlight_destroy(struct backlight *backlight)
137 {
138         if (!backlight)
139                 return;
140
141         if (backlight->path)
142                 free(backlight->path);
143
144         free(backlight);
145 }
146
147 struct backlight *backlight_init(struct udev_device *drm_device,
148                                  uint32_t connector_type)
149 {
150         const char *syspath = NULL;
151         char *pci_name = NULL;
152         char *chosen_path = NULL;
153         char *path = NULL;
154         DIR *backlights;
155         struct dirent *entry;
156         enum backlight_type type = 0;
157         char buffer[100];
158         struct backlight *backlight;
159         int ret;
160
161         if (!drm_device)
162                 return NULL;
163
164         syspath = udev_device_get_syspath(drm_device);
165         if (!syspath)
166                 return NULL;
167
168         if (asprintf(&path, "%s/%s", syspath, "device") < 0)
169                 return NULL;
170
171         ret = readlink(path, buffer, sizeof(buffer) - 1);
172         free(path);
173         if (ret < 0)
174                 return NULL;
175
176         buffer[ret] = '\0';
177         pci_name = basename(buffer);
178
179         if (connector_type <= 0)
180                 return NULL;
181
182         backlights = opendir("/sys/class/backlight");
183         if (!backlights)
184                 return NULL;
185
186         /* Find the "best" backlight for the device. Firmware
187            interfaces are preferred over platform interfaces are
188            preferred over raw interfaces. For raw interfaces we'll
189            check if the device ID in the form of pci match, while
190            for firmware interfaces we require the pci ID to
191            match. It's assumed that platform interfaces always match,
192            since we can't actually associate them with IDs.
193
194            A further awkwardness is that, while it's theoretically
195            possible for an ACPI interface to include support for
196            changing the backlight of external devices, it's unlikely
197            to ever be done. It's effectively impossible for a platform
198            interface to do so. So if we get asked about anything that
199            isn't LVDS or eDP, we pretty much have to require that the
200            control be supplied via a raw interface */
201
202         while ((entry = readdir(backlights))) {
203                 char *backlight_path;
204                 char *parent;
205                 enum backlight_type entry_type;
206                 int fd;
207
208                 if (entry->d_name[0] == '.')
209                         continue;
210
211                 if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
212                              entry->d_name) < 0)
213                         return NULL;
214
215                 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0)
216                         return NULL;
217
218                 fd = open(path, O_RDONLY);
219
220                 if (fd < 0)
221                         goto out;
222
223                 ret = read (fd, &buffer, sizeof(buffer));
224                 close (fd);
225
226                 if (ret < 1)
227                         goto out;
228
229                 buffer[ret] = '\0';
230
231                 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
232                         entry_type = BACKLIGHT_RAW;
233                 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
234                         entry_type = BACKLIGHT_PLATFORM;
235                 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
236                         entry_type = BACKLIGHT_FIRMWARE;
237                 else
238                         goto out;
239
240                 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
241                     connector_type != DRM_MODE_CONNECTOR_eDP) {
242                         /* External displays are assumed to require
243                            gpu control at the moment */
244                         if (entry_type != BACKLIGHT_RAW)
245                                 goto out;
246                 }
247
248                 free (path);
249
250                 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
251                         return NULL;
252
253                 ret = readlink(path, buffer, sizeof(buffer) - 1);
254
255                 if (ret < 0)
256                         goto out;
257
258                 buffer[ret] = '\0';
259
260                 parent = basename(buffer);
261
262                 /* Perform matching for raw and firmware backlights - 
263                    platform backlights have to be assumed to match */
264                 if (entry_type == BACKLIGHT_RAW ||
265                     entry_type == BACKLIGHT_FIRMWARE) {
266                         if (!(pci_name && !strcmp(pci_name, parent)))
267                                 goto out;
268                 }
269
270                 if (entry_type < type)
271                         goto out;
272
273                 type = entry_type;
274
275                 if (chosen_path)
276                         free(chosen_path);
277                 chosen_path = strdup(backlight_path);
278
279         out:
280                 free(backlight_path);
281                 free(path);
282         }
283
284         if (!chosen_path)
285                 return NULL;
286
287         backlight = malloc(sizeof(struct backlight));
288
289         if (!backlight)
290                 goto err;
291
292         backlight->path = chosen_path;
293         backlight->type = type;
294
295         backlight->max_brightness = backlight_get_max_brightness(backlight);
296         if (backlight->max_brightness < 0)
297                 goto err;
298
299         backlight->brightness = backlight_get_actual_brightness(backlight);
300         if (backlight->brightness < 0)
301                 goto err;
302
303         return backlight;
304 err:
305         if (chosen_path)
306                 free(chosen_path);
307         free (backlight);
308         return NULL;
309 }