1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Counter Watch Events - Test various counter watch events in a userspace application
4 *
5 * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
6 * Author: Fabrice Gasnier <fabrice.gasnier@foss.st.com>.
7 */
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <linux/counter.h>
13 #include <linux/kernel.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <sys/ioctl.h>
18 #include <unistd.h>
19
20 static struct counter_watch simple_watch[] = {
21 {
22 /* Component data: Count 0 count */
23 .component.type = COUNTER_COMPONENT_COUNT,
24 .component.scope = COUNTER_SCOPE_COUNT,
25 .component.parent = 0,
26 /* Event type: overflow or underflow */
27 .event = COUNTER_EVENT_OVERFLOW_UNDERFLOW,
28 /* Device event channel 0 */
29 .channel = 0,
30 },
31 };
32
33 static const char * const counter_event_type_name[] = {
34 "COUNTER_EVENT_OVERFLOW",
35 "COUNTER_EVENT_UNDERFLOW",
36 "COUNTER_EVENT_OVERFLOW_UNDERFLOW",
37 "COUNTER_EVENT_THRESHOLD",
38 "COUNTER_EVENT_INDEX",
39 "COUNTER_EVENT_CHANGE_OF_STATE",
40 "COUNTER_EVENT_CAPTURE",
41 };
42
43 static const char * const counter_component_type_name[] = {
44 "COUNTER_COMPONENT_NONE",
45 "COUNTER_COMPONENT_SIGNAL",
46 "COUNTER_COMPONENT_COUNT",
47 "COUNTER_COMPONENT_FUNCTION",
48 "COUNTER_COMPONENT_SYNAPSE_ACTION",
49 "COUNTER_COMPONENT_EXTENSION",
50 };
51
52 static const char * const counter_scope_name[] = {
53 "COUNTER_SCOPE_DEVICE",
54 "COUNTER_SCOPE_SIGNAL",
55 "COUNTER_SCOPE_COUNT",
56 };
57
print_watch(struct counter_watch * watch,int nwatch)58 static void print_watch(struct counter_watch *watch, int nwatch)
59 {
60 int i;
61
62 /* prints the watch array in C-like structure */
63 printf("watch[%d] = {\n", nwatch);
64 for (i = 0; i < nwatch; i++) {
65 printf(" [%d] =\t{\n"
66 "\t\t.component.type = %s\n"
67 "\t\t.component.scope = %s\n"
68 "\t\t.component.parent = %d\n"
69 "\t\t.component.id = %d\n"
70 "\t\t.event = %s\n"
71 "\t\t.channel = %d\n"
72 "\t},\n",
73 i,
74 counter_component_type_name[watch[i].component.type],
75 counter_scope_name[watch[i].component.scope],
76 watch[i].component.parent,
77 watch[i].component.id,
78 counter_event_type_name[watch[i].event],
79 watch[i].channel);
80 }
81 printf("};\n");
82 }
83
print_usage(void)84 static void print_usage(void)
85 {
86 fprintf(stderr, "Usage:\n\n"
87 "counter_watch_events [options] [-w <watchoptions>]\n"
88 "counter_watch_events [options] [-w <watch1 options>] [-w <watch2 options>]...\n"
89 "\n"
90 "When no --watch option has been provided, simple watch example is used:\n"
91 "counter_watch_events [options] -w comp_count,scope_count,evt_ovf_udf\n"
92 "\n"
93 "Test various watch events for given counter device.\n"
94 "\n"
95 "Options:\n"
96 " -d, --debug Prints debug information\n"
97 " -h, --help Prints usage\n"
98 " -n, --device-num <n> Use /dev/counter<n> [default: /dev/counter0]\n"
99 " -l, --loop <n> Loop for <n> events [default: 0 (forever)]\n"
100 " -w, --watch <watchoptions> comma-separated list of watch options\n"
101 "\n"
102 "Watch options:\n"
103 " scope_device (COUNTER_SCOPE_DEVICE) [default: scope_device]\n"
104 " scope_signal (COUNTER_SCOPE_SIGNAL)\n"
105 " scope_count (COUNTER_SCOPE_COUNT)\n"
106 "\n"
107 " comp_none (COUNTER_COMPONENT_NONE) [default: comp_none]\n"
108 " comp_signal (COUNTER_COMPONENT_SIGNAL)\n"
109 " comp_count (COUNTER_COMPONENT_COUNT)\n"
110 " comp_function (COUNTER_COMPONENT_FUNCTION)\n"
111 " comp_synapse_action (COUNTER_COMPONENT_SYNAPSE_ACTION)\n"
112 " comp_extension (COUNTER_COMPONENT_EXTENSION)\n"
113 "\n"
114 " evt_ovf (COUNTER_EVENT_OVERFLOW) [default: evt_ovf]\n"
115 " evt_udf (COUNTER_EVENT_UNDERFLOW)\n"
116 " evt_ovf_udf (COUNTER_EVENT_OVERFLOW_UNDERFLOW)\n"
117 " evt_threshold (COUNTER_EVENT_THRESHOLD)\n"
118 " evt_index (COUNTER_EVENT_INDEX)\n"
119 " evt_change_of_state (COUNTER_EVENT_CHANGE_OF_STATE)\n"
120 " evt_capture (COUNTER_EVENT_CAPTURE)\n"
121 "\n"
122 " chan=<n> channel <n> for this watch [default: 0]\n"
123 " id=<n> component id <n> for this watch [default: 0]\n"
124 " parent=<n> component parent <n> for this watch [default: 0]\n"
125 "\n"
126 "Example with two watched events:\n\n"
127 "counter_watch_events -d \\\n"
128 "\t-w comp_count,scope_count,evt_ovf_udf \\\n"
129 "\t-w comp_extension,scope_count,evt_capture,id=7,chan=3\n"
130 );
131 }
132
133 static const struct option longopts[] = {
134 { "debug", no_argument, 0, 'd' },
135 { "help", no_argument, 0, 'h' },
136 { "device-num", required_argument, 0, 'n' },
137 { "loop", required_argument, 0, 'l' },
138 { "watch", required_argument, 0, 'w' },
139 { },
140 };
141
142 /* counter watch subopts */
143 enum {
144 WATCH_SCOPE_DEVICE,
145 WATCH_SCOPE_SIGNAL,
146 WATCH_SCOPE_COUNT,
147 WATCH_COMPONENT_NONE,
148 WATCH_COMPONENT_SIGNAL,
149 WATCH_COMPONENT_COUNT,
150 WATCH_COMPONENT_FUNCTION,
151 WATCH_COMPONENT_SYNAPSE_ACTION,
152 WATCH_COMPONENT_EXTENSION,
153 WATCH_EVENT_OVERFLOW,
154 WATCH_EVENT_UNDERFLOW,
155 WATCH_EVENT_OVERFLOW_UNDERFLOW,
156 WATCH_EVENT_THRESHOLD,
157 WATCH_EVENT_INDEX,
158 WATCH_EVENT_CHANGE_OF_STATE,
159 WATCH_EVENT_CAPTURE,
160 WATCH_CHANNEL,
161 WATCH_ID,
162 WATCH_PARENT,
163 WATCH_SUBOPTS_MAX,
164 };
165
166 static char * const counter_watch_subopts[WATCH_SUBOPTS_MAX + 1] = {
167 /* component.scope */
168 [WATCH_SCOPE_DEVICE] = "scope_device",
169 [WATCH_SCOPE_SIGNAL] = "scope_signal",
170 [WATCH_SCOPE_COUNT] = "scope_count",
171 /* component.type */
172 [WATCH_COMPONENT_NONE] = "comp_none",
173 [WATCH_COMPONENT_SIGNAL] = "comp_signal",
174 [WATCH_COMPONENT_COUNT] = "comp_count",
175 [WATCH_COMPONENT_FUNCTION] = "comp_function",
176 [WATCH_COMPONENT_SYNAPSE_ACTION] = "comp_synapse_action",
177 [WATCH_COMPONENT_EXTENSION] = "comp_extension",
178 /* event */
179 [WATCH_EVENT_OVERFLOW] = "evt_ovf",
180 [WATCH_EVENT_UNDERFLOW] = "evt_udf",
181 [WATCH_EVENT_OVERFLOW_UNDERFLOW] = "evt_ovf_udf",
182 [WATCH_EVENT_THRESHOLD] = "evt_threshold",
183 [WATCH_EVENT_INDEX] = "evt_index",
184 [WATCH_EVENT_CHANGE_OF_STATE] = "evt_change_of_state",
185 [WATCH_EVENT_CAPTURE] = "evt_capture",
186 /* channel, id, parent */
187 [WATCH_CHANNEL] = "chan",
188 [WATCH_ID] = "id",
189 [WATCH_PARENT] = "parent",
190 /* Empty entry ends the opts array */
191 NULL
192 };
193
main(int argc,char ** argv)194 int main(int argc, char **argv)
195 {
196 int c, fd, i, ret, rc = 0, debug = 0, loop = 0, dev_num = 0, nwatch = 0;
197 struct counter_event event_data;
198 char *device_name = NULL, *subopts, *value;
199 struct counter_watch *watches;
200
201 /*
202 * 1st pass:
203 * - list watch events number to allocate the watch array.
204 * - parse normal options (other than watch options)
205 */
206 while ((c = getopt_long(argc, argv, "dhn:l:w:", longopts, NULL)) != -1) {
207 switch (c) {
208 case 'd':
209 debug = 1;
210 break;
211 case 'h':
212 print_usage();
213 return EXIT_SUCCESS;
214 case 'n':
215 dev_num = strtoul(optarg, NULL, 10);
216 if (errno) {
217 perror("strtol failed: --device-num <n>\n");
218 return EXIT_FAILURE;
219 }
220 break;
221 case 'l':
222 loop = strtol(optarg, NULL, 10);
223 if (errno) {
224 perror("strtol failed: --loop <n>\n");
225 return EXIT_FAILURE;
226 }
227 break;
228 case 'w':
229 nwatch++;
230 break;
231 default:
232 return EXIT_FAILURE;
233 }
234 }
235
236 if (nwatch) {
237 watches = calloc(nwatch, sizeof(*watches));
238 if (!watches) {
239 perror("Error allocating watches\n");
240 return EXIT_FAILURE;
241 }
242 } else {
243 /* default to simple watch example */
244 watches = simple_watch;
245 nwatch = ARRAY_SIZE(simple_watch);
246 }
247
248 /* 2nd pass: parse watch sub-options to fill in watch array */
249 optind = 1;
250 i = 0;
251 while ((c = getopt_long(argc, argv, "dhn:l:w:", longopts, NULL)) != -1) {
252 switch (c) {
253 case 'w':
254 subopts = optarg;
255 while (*subopts != '\0') {
256 ret = getsubopt(&subopts, counter_watch_subopts, &value);
257 switch (ret) {
258 case WATCH_SCOPE_DEVICE:
259 case WATCH_SCOPE_SIGNAL:
260 case WATCH_SCOPE_COUNT:
261 /* match with counter_scope */
262 watches[i].component.scope = ret;
263 break;
264 case WATCH_COMPONENT_NONE:
265 case WATCH_COMPONENT_SIGNAL:
266 case WATCH_COMPONENT_COUNT:
267 case WATCH_COMPONENT_FUNCTION:
268 case WATCH_COMPONENT_SYNAPSE_ACTION:
269 case WATCH_COMPONENT_EXTENSION:
270 /* match counter_component_type: subtract enum value */
271 ret -= WATCH_COMPONENT_NONE;
272 watches[i].component.type = ret;
273 break;
274 case WATCH_EVENT_OVERFLOW:
275 case WATCH_EVENT_UNDERFLOW:
276 case WATCH_EVENT_OVERFLOW_UNDERFLOW:
277 case WATCH_EVENT_THRESHOLD:
278 case WATCH_EVENT_INDEX:
279 case WATCH_EVENT_CHANGE_OF_STATE:
280 case WATCH_EVENT_CAPTURE:
281 /* match counter_event_type: subtract enum value */
282 ret -= WATCH_EVENT_OVERFLOW;
283 watches[i].event = ret;
284 break;
285 case WATCH_CHANNEL:
286 if (!value) {
287 fprintf(stderr, "Invalid chan=<number>\n");
288 rc = EXIT_FAILURE;
289 goto err_free_watches;
290 }
291 watches[i].channel = strtoul(value, NULL, 10);
292 if (errno) {
293 perror("strtoul failed: chan=<number>\n");
294 rc = EXIT_FAILURE;
295 goto err_free_watches;
296 }
297 break;
298 case WATCH_ID:
299 if (!value) {
300 fprintf(stderr, "Invalid id=<number>\n");
301 rc = EXIT_FAILURE;
302 goto err_free_watches;
303 }
304 watches[i].component.id = strtoul(value, NULL, 10);
305 if (errno) {
306 perror("strtoul failed: id=<number>\n");
307 rc = EXIT_FAILURE;
308 goto err_free_watches;
309 }
310 break;
311 case WATCH_PARENT:
312 if (!value) {
313 fprintf(stderr, "Invalid parent=<number>\n");
314 rc = EXIT_FAILURE;
315 goto err_free_watches;
316 }
317 watches[i].component.parent = strtoul(value, NULL, 10);
318 if (errno) {
319 perror("strtoul failed: parent=<number>\n");
320 rc = EXIT_FAILURE;
321 goto err_free_watches;
322 }
323 break;
324 default:
325 fprintf(stderr, "Unknown suboption '%s'\n", value);
326 rc = EXIT_FAILURE;
327 goto err_free_watches;
328 }
329 }
330 i++;
331 break;
332 }
333 }
334
335 if (debug)
336 print_watch(watches, nwatch);
337
338 ret = asprintf(&device_name, "/dev/counter%d", dev_num);
339 if (ret < 0) {
340 fprintf(stderr, "asprintf failed\n");
341 rc = EXIT_FAILURE;
342 goto err_free_watches;
343 }
344
345 if (debug)
346 printf("Opening %s\n", device_name);
347
348 fd = open(device_name, O_RDWR);
349 if (fd == -1) {
350 fprintf(stderr, "Unable to open %s: %s\n", device_name, strerror(errno));
351 free(device_name);
352 rc = EXIT_FAILURE;
353 goto err_free_watches;
354 }
355 free(device_name);
356
357 for (i = 0; i < nwatch; i++) {
358 ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i);
359 if (ret == -1) {
360 fprintf(stderr, "Error adding watches[%d]: %s\n", i,
361 strerror(errno));
362 rc = EXIT_FAILURE;
363 goto err_close;
364 }
365 }
366
367 ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
368 if (ret == -1) {
369 perror("Error enabling events");
370 rc = EXIT_FAILURE;
371 goto err_close;
372 }
373
374 for (i = 0; loop <= 0 || i < loop; i++) {
375 ret = read(fd, &event_data, sizeof(event_data));
376 if (ret == -1) {
377 perror("Failed to read event data");
378 rc = EXIT_FAILURE;
379 goto err_close;
380 }
381
382 if (ret != sizeof(event_data)) {
383 fprintf(stderr, "Failed to read event data (got: %d)\n", ret);
384 rc = EXIT_FAILURE;
385 goto err_close;
386 }
387
388 printf("Timestamp: %llu\tData: %llu\t event: %s\tch: %d\n",
389 event_data.timestamp, event_data.value,
390 counter_event_type_name[event_data.watch.event],
391 event_data.watch.channel);
392
393 if (event_data.status) {
394 fprintf(stderr, "Error %d: %s\n", event_data.status,
395 strerror(event_data.status));
396 }
397 }
398
399 err_close:
400 close(fd);
401 err_free_watches:
402 if (watches != simple_watch)
403 free(watches);
404
405 return rc;
406 }
407