3898fe7e35ece355785ebdf157b52aea9f925b97
[linux-2.6-microblaze.git] / tools / counter / counter_watch_events.c
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
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
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>                     componend id <n> for this watch [default: 0]\n"
124                 "  parent=<n>                 componend 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
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 }