0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #define KMSG_COMPONENT "appldata"
0013 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0014
0015 #include <linux/module.h>
0016 #include <linux/sched/stat.h>
0017 #include <linux/init.h>
0018 #include <linux/slab.h>
0019 #include <linux/errno.h>
0020 #include <linux/interrupt.h>
0021 #include <linux/proc_fs.h>
0022 #include <linux/mm.h>
0023 #include <linux/swap.h>
0024 #include <linux/pagemap.h>
0025 #include <linux/sysctl.h>
0026 #include <linux/notifier.h>
0027 #include <linux/cpu.h>
0028 #include <linux/workqueue.h>
0029 #include <linux/suspend.h>
0030 #include <linux/platform_device.h>
0031 #include <asm/appldata.h>
0032 #include <asm/vtimer.h>
0033 #include <linux/uaccess.h>
0034 #include <asm/io.h>
0035 #include <asm/smp.h>
0036
0037 #include "appldata.h"
0038
0039
0040 #define APPLDATA_CPU_INTERVAL 10000
0041
0042
0043
0044 #define TOD_MICRO 0x01000
0045
0046
0047 static struct platform_device *appldata_pdev;
0048
0049
0050
0051
0052 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
0053 static int appldata_timer_handler(struct ctl_table *ctl, int write,
0054 void *buffer, size_t *lenp, loff_t *ppos);
0055 static int appldata_interval_handler(struct ctl_table *ctl, int write,
0056 void *buffer, size_t *lenp, loff_t *ppos);
0057
0058 static struct ctl_table_header *appldata_sysctl_header;
0059 static struct ctl_table appldata_table[] = {
0060 {
0061 .procname = "timer",
0062 .mode = S_IRUGO | S_IWUSR,
0063 .proc_handler = appldata_timer_handler,
0064 },
0065 {
0066 .procname = "interval",
0067 .mode = S_IRUGO | S_IWUSR,
0068 .proc_handler = appldata_interval_handler,
0069 },
0070 { },
0071 };
0072
0073 static struct ctl_table appldata_dir_table[] = {
0074 {
0075 .procname = appldata_proc_name,
0076 .maxlen = 0,
0077 .mode = S_IRUGO | S_IXUGO,
0078 .child = appldata_table,
0079 },
0080 { },
0081 };
0082
0083
0084
0085
0086 static struct vtimer_list appldata_timer;
0087
0088 static DEFINE_SPINLOCK(appldata_timer_lock);
0089 static int appldata_interval = APPLDATA_CPU_INTERVAL;
0090 static int appldata_timer_active;
0091 static int appldata_timer_suspended = 0;
0092
0093
0094
0095
0096 static struct workqueue_struct *appldata_wq;
0097 static void appldata_work_fn(struct work_struct *work);
0098 static DECLARE_WORK(appldata_work, appldata_work_fn);
0099
0100
0101
0102
0103
0104 static DEFINE_MUTEX(appldata_ops_mutex);
0105 static LIST_HEAD(appldata_ops_list);
0106
0107
0108
0109
0110
0111
0112
0113
0114 static void appldata_timer_function(unsigned long data)
0115 {
0116 queue_work(appldata_wq, (struct work_struct *) data);
0117 }
0118
0119
0120
0121
0122
0123
0124 static void appldata_work_fn(struct work_struct *work)
0125 {
0126 struct list_head *lh;
0127 struct appldata_ops *ops;
0128
0129 mutex_lock(&appldata_ops_mutex);
0130 list_for_each(lh, &appldata_ops_list) {
0131 ops = list_entry(lh, struct appldata_ops, list);
0132 if (ops->active == 1) {
0133 ops->callback(ops->data);
0134 }
0135 }
0136 mutex_unlock(&appldata_ops_mutex);
0137 }
0138
0139 static struct appldata_product_id appldata_id = {
0140 .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,
0141 0xE7, 0xD2, 0xD9},
0142 .prod_fn = 0xD5D3,
0143 .version_nr = 0xF2F6,
0144 .release_nr = 0xF0F1,
0145 };
0146
0147
0148
0149
0150
0151
0152 int appldata_diag(char record_nr, u16 function, unsigned long buffer,
0153 u16 length, char *mod_lvl)
0154 {
0155 struct appldata_parameter_list *parm_list;
0156 struct appldata_product_id *id;
0157 int rc;
0158
0159 parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
0160 id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL);
0161 rc = -ENOMEM;
0162 if (parm_list && id) {
0163 id->record_nr = record_nr;
0164 id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
0165 rc = appldata_asm(parm_list, id, function,
0166 (void *) buffer, length);
0167 }
0168 kfree(id);
0169 kfree(parm_list);
0170 return rc;
0171 }
0172
0173
0174
0175
0176
0177 #define APPLDATA_ADD_TIMER 0
0178 #define APPLDATA_DEL_TIMER 1
0179 #define APPLDATA_MOD_TIMER 2
0180
0181
0182
0183
0184
0185
0186
0187 static void __appldata_vtimer_setup(int cmd)
0188 {
0189 u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
0190
0191 switch (cmd) {
0192 case APPLDATA_ADD_TIMER:
0193 if (appldata_timer_active)
0194 break;
0195 appldata_timer.expires = timer_interval;
0196 add_virt_timer_periodic(&appldata_timer);
0197 appldata_timer_active = 1;
0198 break;
0199 case APPLDATA_DEL_TIMER:
0200 del_virt_timer(&appldata_timer);
0201 if (!appldata_timer_active)
0202 break;
0203 appldata_timer_active = 0;
0204 break;
0205 case APPLDATA_MOD_TIMER:
0206 if (!appldata_timer_active)
0207 break;
0208 mod_virt_timer_periodic(&appldata_timer, timer_interval);
0209 }
0210 }
0211
0212
0213
0214
0215
0216
0217 static int
0218 appldata_timer_handler(struct ctl_table *ctl, int write,
0219 void *buffer, size_t *lenp, loff_t *ppos)
0220 {
0221 int timer_active = appldata_timer_active;
0222 int rc;
0223 struct ctl_table ctl_entry = {
0224 .procname = ctl->procname,
0225 .data = &timer_active,
0226 .maxlen = sizeof(int),
0227 .extra1 = SYSCTL_ZERO,
0228 .extra2 = SYSCTL_ONE,
0229 };
0230
0231 rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
0232 if (rc < 0 || !write)
0233 return rc;
0234
0235 spin_lock(&appldata_timer_lock);
0236 if (timer_active)
0237 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
0238 else
0239 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
0240 spin_unlock(&appldata_timer_lock);
0241 return 0;
0242 }
0243
0244
0245
0246
0247
0248
0249
0250 static int
0251 appldata_interval_handler(struct ctl_table *ctl, int write,
0252 void *buffer, size_t *lenp, loff_t *ppos)
0253 {
0254 int interval = appldata_interval;
0255 int rc;
0256 struct ctl_table ctl_entry = {
0257 .procname = ctl->procname,
0258 .data = &interval,
0259 .maxlen = sizeof(int),
0260 .extra1 = SYSCTL_ONE,
0261 };
0262
0263 rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
0264 if (rc < 0 || !write)
0265 return rc;
0266
0267 spin_lock(&appldata_timer_lock);
0268 appldata_interval = interval;
0269 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
0270 spin_unlock(&appldata_timer_lock);
0271 return 0;
0272 }
0273
0274
0275
0276
0277
0278
0279
0280 static int
0281 appldata_generic_handler(struct ctl_table *ctl, int write,
0282 void *buffer, size_t *lenp, loff_t *ppos)
0283 {
0284 struct appldata_ops *ops = NULL, *tmp_ops;
0285 struct list_head *lh;
0286 int rc, found;
0287 int active;
0288 struct ctl_table ctl_entry = {
0289 .data = &active,
0290 .maxlen = sizeof(int),
0291 .extra1 = SYSCTL_ZERO,
0292 .extra2 = SYSCTL_ONE,
0293 };
0294
0295 found = 0;
0296 mutex_lock(&appldata_ops_mutex);
0297 list_for_each(lh, &appldata_ops_list) {
0298 tmp_ops = list_entry(lh, struct appldata_ops, list);
0299 if (&tmp_ops->ctl_table[2] == ctl) {
0300 found = 1;
0301 }
0302 }
0303 if (!found) {
0304 mutex_unlock(&appldata_ops_mutex);
0305 return -ENODEV;
0306 }
0307 ops = ctl->data;
0308 if (!try_module_get(ops->owner)) {
0309 mutex_unlock(&appldata_ops_mutex);
0310 return -ENODEV;
0311 }
0312 mutex_unlock(&appldata_ops_mutex);
0313
0314 active = ops->active;
0315 rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
0316 if (rc < 0 || !write) {
0317 module_put(ops->owner);
0318 return rc;
0319 }
0320
0321 mutex_lock(&appldata_ops_mutex);
0322 if (active && (ops->active == 0)) {
0323
0324 if (!try_module_get(ops->owner)) {
0325 mutex_unlock(&appldata_ops_mutex);
0326 module_put(ops->owner);
0327 return -ENODEV;
0328 }
0329 ops->callback(ops->data);
0330 rc = appldata_diag(ops->record_nr,
0331 APPLDATA_START_INTERVAL_REC,
0332 (unsigned long) ops->data, ops->size,
0333 ops->mod_lvl);
0334 if (rc != 0) {
0335 pr_err("Starting the data collection for %s "
0336 "failed with rc=%d\n", ops->name, rc);
0337 module_put(ops->owner);
0338 } else
0339 ops->active = 1;
0340 } else if (!active && (ops->active == 1)) {
0341 ops->active = 0;
0342 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
0343 (unsigned long) ops->data, ops->size,
0344 ops->mod_lvl);
0345 if (rc != 0)
0346 pr_err("Stopping the data collection for %s "
0347 "failed with rc=%d\n", ops->name, rc);
0348 module_put(ops->owner);
0349 }
0350 mutex_unlock(&appldata_ops_mutex);
0351 module_put(ops->owner);
0352 return 0;
0353 }
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364 int appldata_register_ops(struct appldata_ops *ops)
0365 {
0366 if (ops->size > APPLDATA_MAX_REC_SIZE)
0367 return -EINVAL;
0368
0369 ops->ctl_table = kcalloc(4, sizeof(struct ctl_table), GFP_KERNEL);
0370 if (!ops->ctl_table)
0371 return -ENOMEM;
0372
0373 mutex_lock(&appldata_ops_mutex);
0374 list_add(&ops->list, &appldata_ops_list);
0375 mutex_unlock(&appldata_ops_mutex);
0376
0377 ops->ctl_table[0].procname = appldata_proc_name;
0378 ops->ctl_table[0].maxlen = 0;
0379 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO;
0380 ops->ctl_table[0].child = &ops->ctl_table[2];
0381
0382 ops->ctl_table[2].procname = ops->name;
0383 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR;
0384 ops->ctl_table[2].proc_handler = appldata_generic_handler;
0385 ops->ctl_table[2].data = ops;
0386
0387 ops->sysctl_header = register_sysctl_table(ops->ctl_table);
0388 if (!ops->sysctl_header)
0389 goto out;
0390 return 0;
0391 out:
0392 mutex_lock(&appldata_ops_mutex);
0393 list_del(&ops->list);
0394 mutex_unlock(&appldata_ops_mutex);
0395 kfree(ops->ctl_table);
0396 return -ENOMEM;
0397 }
0398
0399
0400
0401
0402
0403
0404 void appldata_unregister_ops(struct appldata_ops *ops)
0405 {
0406 mutex_lock(&appldata_ops_mutex);
0407 list_del(&ops->list);
0408 mutex_unlock(&appldata_ops_mutex);
0409 unregister_sysctl_table(ops->sysctl_header);
0410 kfree(ops->ctl_table);
0411 }
0412
0413
0414
0415
0416 static int appldata_freeze(struct device *dev)
0417 {
0418 struct appldata_ops *ops;
0419 int rc;
0420 struct list_head *lh;
0421
0422 spin_lock(&appldata_timer_lock);
0423 if (appldata_timer_active) {
0424 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
0425 appldata_timer_suspended = 1;
0426 }
0427 spin_unlock(&appldata_timer_lock);
0428
0429 mutex_lock(&appldata_ops_mutex);
0430 list_for_each(lh, &appldata_ops_list) {
0431 ops = list_entry(lh, struct appldata_ops, list);
0432 if (ops->active == 1) {
0433 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
0434 (unsigned long) ops->data, ops->size,
0435 ops->mod_lvl);
0436 if (rc != 0)
0437 pr_err("Stopping the data collection for %s "
0438 "failed with rc=%d\n", ops->name, rc);
0439 }
0440 }
0441 mutex_unlock(&appldata_ops_mutex);
0442 return 0;
0443 }
0444
0445 static int appldata_restore(struct device *dev)
0446 {
0447 struct appldata_ops *ops;
0448 int rc;
0449 struct list_head *lh;
0450
0451 spin_lock(&appldata_timer_lock);
0452 if (appldata_timer_suspended) {
0453 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
0454 appldata_timer_suspended = 0;
0455 }
0456 spin_unlock(&appldata_timer_lock);
0457
0458 mutex_lock(&appldata_ops_mutex);
0459 list_for_each(lh, &appldata_ops_list) {
0460 ops = list_entry(lh, struct appldata_ops, list);
0461 if (ops->active == 1) {
0462 ops->callback(ops->data);
0463 rc = appldata_diag(ops->record_nr,
0464 APPLDATA_START_INTERVAL_REC,
0465 (unsigned long) ops->data, ops->size,
0466 ops->mod_lvl);
0467 if (rc != 0) {
0468 pr_err("Starting the data collection for %s "
0469 "failed with rc=%d\n", ops->name, rc);
0470 }
0471 }
0472 }
0473 mutex_unlock(&appldata_ops_mutex);
0474 return 0;
0475 }
0476
0477 static int appldata_thaw(struct device *dev)
0478 {
0479 return appldata_restore(dev);
0480 }
0481
0482 static const struct dev_pm_ops appldata_pm_ops = {
0483 .freeze = appldata_freeze,
0484 .thaw = appldata_thaw,
0485 .restore = appldata_restore,
0486 };
0487
0488 static struct platform_driver appldata_pdrv = {
0489 .driver = {
0490 .name = "appldata",
0491 .pm = &appldata_pm_ops,
0492 },
0493 };
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503
0504 static int __init appldata_init(void)
0505 {
0506 int rc;
0507
0508 init_virt_timer(&appldata_timer);
0509 appldata_timer.function = appldata_timer_function;
0510 appldata_timer.data = (unsigned long) &appldata_work;
0511
0512 rc = platform_driver_register(&appldata_pdrv);
0513 if (rc)
0514 return rc;
0515
0516 appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
0517 0);
0518 if (IS_ERR(appldata_pdev)) {
0519 rc = PTR_ERR(appldata_pdev);
0520 goto out_driver;
0521 }
0522 appldata_wq = alloc_ordered_workqueue("appldata", 0);
0523 if (!appldata_wq) {
0524 rc = -ENOMEM;
0525 goto out_device;
0526 }
0527
0528 appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
0529 return 0;
0530
0531 out_device:
0532 platform_device_unregister(appldata_pdev);
0533 out_driver:
0534 platform_driver_unregister(&appldata_pdrv);
0535 return rc;
0536 }
0537
0538 __initcall(appldata_init);
0539
0540
0541
0542 EXPORT_SYMBOL_GPL(appldata_register_ops);
0543 EXPORT_SYMBOL_GPL(appldata_unregister_ops);
0544 EXPORT_SYMBOL_GPL(appldata_diag);
0545
0546 #ifdef CONFIG_SWAP
0547 EXPORT_SYMBOL_GPL(si_swapinfo);
0548 #endif
0549 EXPORT_SYMBOL_GPL(nr_threads);
0550 EXPORT_SYMBOL_GPL(nr_running);
0551 EXPORT_SYMBOL_GPL(nr_iowait);