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