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
20static 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
33static 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
43static 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
52static const char * const counter_scope_name[] = {
53	"COUNTER_SCOPE_DEVICE",
54	"COUNTER_SCOPE_SIGNAL",
55	"COUNTER_SCOPE_COUNT",
56};
57
58static 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
84static 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
133static 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 */
143enum {
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
166static 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
194int 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
399err_close:
400	close(fd);
401err_free_watches:
402	if (watches != simple_watch)
403		free(watches);
404
405	return rc;
406}
407