1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Simple heartbeat STM source driver |
4 | * Copyright (c) 2016, Intel Corporation. |
5 | * |
6 | * Heartbeat STM source will send repetitive messages over STM devices to a |
7 | * trace host. |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/hrtimer.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/stm.h> |
15 | |
16 | #define STM_HEARTBEAT_MAX 32 |
17 | |
18 | static int nr_devs = 4; |
19 | static int interval_ms = 10; |
20 | |
21 | module_param(nr_devs, int, 0400); |
22 | module_param(interval_ms, int, 0600); |
23 | |
24 | static struct stm_heartbeat { |
25 | struct stm_source_data data; |
26 | struct hrtimer hrtimer; |
27 | unsigned int active; |
28 | } stm_heartbeat[STM_HEARTBEAT_MAX]; |
29 | |
30 | static const char str[] = "heartbeat stm source driver is here to serve you" ; |
31 | |
32 | static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) |
33 | { |
34 | struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat, |
35 | hrtimer); |
36 | |
37 | stm_source_write(data: &heartbeat->data, chan: 0, buf: str, count: sizeof str); |
38 | if (heartbeat->active) |
39 | hrtimer_forward_now(timer: hr, interval: ms_to_ktime(ms: interval_ms)); |
40 | |
41 | return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART; |
42 | } |
43 | |
44 | static int stm_heartbeat_link(struct stm_source_data *data) |
45 | { |
46 | struct stm_heartbeat *heartbeat = |
47 | container_of(data, struct stm_heartbeat, data); |
48 | |
49 | heartbeat->active = 1; |
50 | hrtimer_start(timer: &heartbeat->hrtimer, tim: ms_to_ktime(ms: interval_ms), |
51 | mode: HRTIMER_MODE_ABS); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static void stm_heartbeat_unlink(struct stm_source_data *data) |
57 | { |
58 | struct stm_heartbeat *heartbeat = |
59 | container_of(data, struct stm_heartbeat, data); |
60 | |
61 | heartbeat->active = 0; |
62 | hrtimer_cancel(timer: &heartbeat->hrtimer); |
63 | } |
64 | |
65 | static int stm_heartbeat_init(void) |
66 | { |
67 | int i, ret; |
68 | |
69 | if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX) |
70 | return -EINVAL; |
71 | |
72 | for (i = 0; i < nr_devs; i++) { |
73 | stm_heartbeat[i].data.name = |
74 | kasprintf(GFP_KERNEL, fmt: "heartbeat.%d" , i); |
75 | if (!stm_heartbeat[i].data.name) { |
76 | ret = -ENOMEM; |
77 | goto fail_unregister; |
78 | } |
79 | |
80 | stm_heartbeat[i].data.nr_chans = 1; |
81 | stm_heartbeat[i].data.link = stm_heartbeat_link; |
82 | stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; |
83 | hrtimer_init(timer: &stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, |
84 | mode: HRTIMER_MODE_ABS); |
85 | stm_heartbeat[i].hrtimer.function = |
86 | stm_heartbeat_hrtimer_handler; |
87 | |
88 | ret = stm_source_register_device(NULL, data: &stm_heartbeat[i].data); |
89 | if (ret) |
90 | goto fail_free; |
91 | } |
92 | |
93 | return 0; |
94 | |
95 | fail_unregister: |
96 | for (i--; i >= 0; i--) { |
97 | stm_source_unregister_device(data: &stm_heartbeat[i].data); |
98 | fail_free: |
99 | kfree(objp: stm_heartbeat[i].data.name); |
100 | } |
101 | |
102 | return ret; |
103 | } |
104 | |
105 | static void stm_heartbeat_exit(void) |
106 | { |
107 | int i; |
108 | |
109 | for (i = 0; i < nr_devs; i++) { |
110 | stm_source_unregister_device(data: &stm_heartbeat[i].data); |
111 | kfree(objp: stm_heartbeat[i].data.name); |
112 | } |
113 | } |
114 | |
115 | module_init(stm_heartbeat_init); |
116 | module_exit(stm_heartbeat_exit); |
117 | |
118 | MODULE_LICENSE("GPL v2" ); |
119 | MODULE_DESCRIPTION("stm_heartbeat driver" ); |
120 | MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>" ); |
121 | |