[REFACTOR] Buffer: move getting next queue element into separate function
[kernel/swap-modules.git] / energy / lcd / lcd_base.c
1 /*
2  *  Dynamic Binary Instrumentation Module based on KProbes
3  *  energy/lcd/lcd_base.c
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 the
13  * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2013
20  *
21  * 2013         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
22  *
23  */
24
25
26 #include <linux/module.h>
27 #include <linux/slab.h>
28 #include <linux/fs.h>
29 #include <energy/tm_stat.h>
30 #include "lcd_base.h"
31 #include "lcd_debugfs.h"
32
33
34 int read_val(const char *path)
35 {
36         int ret;
37         struct file *f;
38         unsigned long val;
39         enum { buf_len = 32 };
40         char buf[buf_len];
41
42         f = filp_open(path, O_RDONLY, 0);
43         if (IS_ERR(f)) {
44                 printk("cannot open file \'%s\'", path);
45                 return PTR_ERR(f);
46         }
47
48         ret = kernel_read(f, 0, buf, sizeof(buf));
49         filp_close(f, NULL);
50         if (ret < 0)
51                 return ret;
52
53         buf[ret >= buf_len ? buf_len - 1 : ret] = '\0';
54
55         ret = strict_strtoul(buf, 0, &val);
56         if (ret)
57                 return ret;
58
59         return (int)val;
60 }
61
62 enum {
63         brt_no_init = -1,
64         brt_cnt = 10
65 };
66
67 struct lcd_priv_data {
68         int min_brt;
69         int max_brt;
70
71         size_t tms_brt_cnt;
72         struct tm_stat *tms_brt;
73         spinlock_t lock_tms;
74         int brt_old;
75
76         u64 min_denom;
77         u64 min_num;
78         u64 max_denom;
79         u64 max_num;
80 };
81
82 static void *create_lcd_priv(struct lcd_ops *ops, size_t tms_brt_cnt)
83 {
84         int i;
85         struct lcd_priv_data *lcd;
86
87         if (tms_brt_cnt <= 0) {
88                 printk("error variable tms_brt_cnt=%d\n", tms_brt_cnt);
89                 return NULL;
90         }
91
92         lcd = kmalloc(sizeof(*lcd) + sizeof(*lcd->tms_brt) * tms_brt_cnt,
93                       GFP_KERNEL);
94         if (lcd == NULL) {
95                 printk("error: %s - out of memory\n", __func__);
96                 return NULL;
97         }
98
99         lcd->tms_brt = (void *)lcd + sizeof(*lcd);
100         lcd->tms_brt_cnt = tms_brt_cnt;
101
102         lcd->min_brt = ops->get(ops, LPD_MIN_BRIGHTNESS);
103         lcd->max_brt = ops->get(ops, LPD_MAX_BRIGHTNESS);
104
105         for (i = 0; i < tms_brt_cnt; ++i)
106                 tm_stat_init(&lcd->tms_brt[i]);
107
108         spin_lock_init(&lcd->lock_tms);
109
110         lcd->brt_old = brt_no_init;
111
112         lcd->min_denom = 1;
113         lcd->min_num = 1;
114         lcd->max_denom = 1;
115         lcd->max_num = 1;
116
117         return (void *)lcd;
118 }
119
120 static void destroy_lcd_priv(void *data)
121 {
122         kfree(data);
123 }
124
125 static struct lcd_priv_data *get_lcd_priv(struct lcd_ops *ops)
126 {
127         return (struct lcd_priv_data *)ops->priv;
128 }
129
130 static void clean_brightness(struct lcd_ops *ops)
131 {
132         struct lcd_priv_data *lcd = get_lcd_priv(ops);
133         int i;
134
135         spin_lock(&lcd->lock_tms);
136         for (i = 0; i < lcd->tms_brt_cnt; ++i)
137                 tm_stat_init(&lcd->tms_brt[i]);
138
139         lcd->brt_old = brt_no_init;
140         spin_unlock(&lcd->lock_tms);
141 }
142
143 static void set_brightness(struct lcd_ops *ops, int brt)
144 {
145         struct lcd_priv_data *lcd = get_lcd_priv(ops);
146         int n;
147
148         if (brt > lcd->max_brt || brt < lcd->min_brt) {
149                 printk("LCD energy error: set brightness=%d, "
150                        "when brightness[%d..%d]\n",
151                        brt, lcd->min_brt, lcd->max_brt);
152                 brt = brt > lcd->max_brt ? lcd->max_brt : lcd->min_brt;
153         }
154
155         n = lcd->tms_brt_cnt * (brt - lcd->min_brt) /
156             (lcd->max_brt - lcd->min_brt + 1);
157
158         spin_lock(&lcd->lock_tms);
159         if (lcd->brt_old != n) {
160                 u64 time = get_ntime();
161                 if (lcd->brt_old != brt_no_init)
162                         tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
163
164                 tm_stat_set_timestamp(&lcd->tms_brt[n], time);
165                 lcd->brt_old = n;
166         }
167         spin_unlock(&lcd->lock_tms);
168 }
169
170 static int func_notifier_lcd(struct lcd_ops *ops, enum lcd_action_type action,
171                              void *data)
172 {
173         switch (action) {
174         case LAT_BRIGHTNESS:
175                 set_brightness(ops, (int)data);
176                 break;
177         default:
178                 return -EINVAL;
179         }
180
181         return 0;
182 }
183
184 size_t get_lcd_size_array(struct lcd_ops *ops)
185 {
186         struct lcd_priv_data *lcd = get_lcd_priv(ops);
187
188         return lcd->tms_brt_cnt;
189 }
190
191 void get_lcd_array_time(struct lcd_ops *ops, u64 *array_time)
192 {
193         struct lcd_priv_data *lcd = get_lcd_priv(ops);
194         int i;
195
196         spin_lock(&lcd->lock_tms);
197         for (i = 0; i < lcd->tms_brt_cnt; ++i) {
198                 array_time[i] = tm_stat_running(&lcd->tms_brt[i]);
199                 if (i == lcd->brt_old)
200                         array_time[i] += get_ntime() -
201                                          tm_stat_timestamp(&lcd->tms_brt[i]);
202         }
203         spin_unlock(&lcd->lock_tms);
204 }
205
206 static int register_lcd(struct lcd_ops *ops)
207 {
208         int ret = 0;
209
210         ops->priv = create_lcd_priv(ops, brt_cnt);
211
212         /* TODO: create init_func() for 'struct rational' */
213         ops->min_coef.num = 1;
214         ops->min_coef.denom = 1;
215         ops->max_coef.num = 1;
216         ops->max_coef.denom = 1;
217
218         ops->notifier = func_notifier_lcd;
219
220         ret = register_lcd_debugfs(ops);
221         if (ret)
222                 destroy_lcd_priv(ops->priv);
223
224         return ret;
225 }
226
227 static void unregister_lcd(struct lcd_ops *ops)
228 {
229         unregister_lcd_debugfs(ops);
230         destroy_lcd_priv(ops->priv);
231 }
232
233
234
235
236 /* ============================================================================
237  * ===                          LCD_INIT/LCD_EXIT                           ===
238  * ============================================================================
239  */
240 typedef struct lcd_ops *(*get_ops_t)(void);
241
242 DEFINITION_LCD_FUNC;
243
244 get_ops_t lcd_ops[] = DEFINITION_LCD_ARRAY;
245 enum { lcd_ops_cnt = sizeof(lcd_ops) / sizeof(get_ops_t) };
246
247 enum ST_LCD_OPS {
248         SLO_REGISTER    = 1 << 0,
249         SLO_SET         = 1 << 1
250 };
251
252 static DEFINE_MUTEX(lcd_lock);
253 static enum ST_LCD_OPS stat_lcd_ops[lcd_ops_cnt];
254
255 void lcd_exit(void)
256 {
257         int i;
258         struct lcd_ops *ops;
259
260         mutex_lock(&lcd_lock);
261         for (i = 0; i < lcd_ops_cnt; ++i) {
262                 ops = lcd_ops[i]();
263
264                 if (stat_lcd_ops[i] & SLO_SET) {
265                         ops->unset(ops);
266                         stat_lcd_ops[i] &= ~SLO_SET;
267                 }
268
269                 if (stat_lcd_ops[i] & SLO_REGISTER) {
270                         unregister_lcd(ops);
271                         stat_lcd_ops[i] &= ~SLO_REGISTER;
272                 }
273         }
274         mutex_unlock(&lcd_lock);
275 }
276
277 int lcd_init(void)
278 {
279         int i, ret, count = 0;
280         struct lcd_ops *ops;
281
282         mutex_lock(&lcd_lock);
283         for (i = 0; i < lcd_ops_cnt; ++i) {
284                 ops = lcd_ops[i]();
285                 if (ops == NULL) {
286                         printk("error %s [ops == NULL]\n", __func__);
287                         continue;
288                 }
289
290                 if (0 == ops->check(ops)) {
291                         printk("error checking %s\n", ops->name);
292                         continue;
293                 }
294
295                 ret = register_lcd(ops);
296                 if (ret) {
297                         printk("error register_lcd %s\n", ops->name);
298                         continue;
299                 }
300
301                 stat_lcd_ops[i] |= SLO_REGISTER;
302                 ++count;
303         }
304         mutex_unlock(&lcd_lock);
305
306         return count ? 0 : -EPERM;
307 }
308
309
310
311
312
313 /* ============================================================================
314  * ===                     LCD_SET_ENERGY/LCD_UNSET_ENERGY                  ===
315  * ============================================================================
316  */
317 int lcd_set_energy(void)
318 {
319         int i, ret, count = 0;
320         struct lcd_ops *ops;
321
322         mutex_lock(&lcd_lock);
323         for (i = 0; i < lcd_ops_cnt; ++i) {
324                 ops = lcd_ops[i]();
325                 if (stat_lcd_ops[i] & SLO_REGISTER) {
326                         ret = ops->set(ops);
327                         if (ret) {
328                                 printk("error %s set LCD energy", ops->name);
329                                 continue;
330                         }
331
332                         set_brightness(ops, ops->get(ops, LPD_BRIGHTNESS));
333
334                         stat_lcd_ops[i] |= SLO_SET;
335                         ++count;
336                 }
337         }
338         mutex_unlock(&lcd_lock);
339
340         return count ? 0 : -EPERM;
341 }
342
343 void lcd_unset_energy(void)
344 {
345         int i, ret;
346         struct lcd_ops *ops;
347
348         mutex_lock(&lcd_lock);
349         for (i = 0; i < lcd_ops_cnt; ++i) {
350                 ops = lcd_ops[i]();
351                 if (stat_lcd_ops[i] & SLO_SET) {
352                         ret = ops->unset(ops);
353                         if (ret)
354                                 printk("error %s unset LCD energy", ops->name);
355
356                         clean_brightness(ops);
357                         stat_lcd_ops[i] &= ~SLO_SET;
358                 }
359         }
360         mutex_unlock(&lcd_lock);
361 }