1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <ctype.h>
6 #include <dirent.h>
7 #include <fcntl.h>
8 #include <getopt.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <zircon/device/clk.h>
15 
usage(const char * cmd)16 int usage(const char* cmd) {
17     fprintf(
18         stderr,
19         "\nInteract with clocks on the SOC:\n"
20         "   %s measure                    Measures all clock values\n"
21         "   %s measure -idx <idx>         Measure CLK idx\n"
22         "   %s help                       Print this message\n",
23         cmd,
24         cmd,
25         cmd);
26     return -1;
27 }
28 
29 // Returns "true" if the argument matches the prefix.
30 // In this case, moves the argument past the prefix.
prefix_match(const char ** arg,const char * prefix)31 bool prefix_match(const char** arg, const char* prefix) {
32     if (!strncmp(*arg, prefix, strlen(prefix))) {
33         *arg += strlen(prefix);
34         return true;
35     }
36     return false;
37 }
38 
39 // Gets the value of a particular field passed through
40 // command line.
getValue(int argc,char ** argv,const char * field)41 const char* getValue(int argc, char** argv, const char* field) {
42     int i = 1;
43     while (i < argc - 1 && strcmp(argv[i], field) != 0) {
44         ++i;
45     }
46     if (i >= argc - 1) {
47         printf("NULL\n");
48         return NULL;
49     } else {
50         return argv[i + 1];
51     }
52 }
53 
guess_dev(void)54 char* guess_dev(void) {
55     char path[21]; // strlen("/dev/class/clock/###") + 1
56     DIR* d = opendir("/dev/class/clock");
57     if (!d) {
58         return NULL;
59     }
60 
61     struct dirent* de;
62     while ((de = readdir(d)) != NULL) {
63         if (strlen(de->d_name) != 3) {
64             continue;
65         }
66 
67         if (isdigit(de->d_name[0]) &&
68             isdigit(de->d_name[1]) &&
69             isdigit(de->d_name[2])) {
70             sprintf(path, "/dev/class/clock/%.3s", de->d_name);
71             closedir(d);
72             return strdup(path);
73         }
74     }
75 
76     closedir(d);
77     return NULL;
78 }
79 
measure_clk_util(int fd,uint32_t idx)80 int measure_clk_util(int fd, uint32_t idx) {
81     clk_freq_info_t info;
82     ssize_t rc = ioctl_clk_measure(fd, &idx, &info);
83     if (rc < 0) {
84         fprintf(stderr, "ERROR: Failed to measure clock: %zd\n", rc);
85         return rc;
86     }
87     printf("[%4d][%4d MHz] %s\n", idx, info.clk_freq, info.clk_name);
88     return 0;
89 }
90 
measure_clk(const char * path,uint32_t idx,bool clk)91 int measure_clk(const char* path, uint32_t idx, bool clk) {
92     int fd = open(path, O_RDWR);
93     if (fd < 0) {
94         fprintf(stderr, "ERROR: Failed to open clock device: %d\n", fd);
95         return -1;
96     }
97 
98     uint32_t num_clocks = 0;
99     ssize_t rc = ioctl_clk_get_count(fd, &num_clocks);
100     if (rc < 0) {
101         fprintf(stderr, "ERROR: Failed to get num_clocks: %zd\n", rc);
102         return rc;
103     }
104 
105     if (clk) {
106         if (idx > num_clocks) {
107             fprintf(stderr, "ERROR: Invalid clock index.\n");
108             return -1;
109         }
110         return measure_clk_util(fd, idx);
111     } else {
112         for (uint32_t i = 0; i < num_clocks; i++) {
113             rc = measure_clk_util(fd, i);
114             if (rc < 0) {
115                 return rc;
116             }
117         }
118     }
119     return 0;
120 }
121 
main(int argc,char ** argv)122 int main(int argc, char** argv) {
123     int err = 0;
124     const char* cmd = basename(argv[0]);
125     char* path = NULL;
126     const char* index = NULL;
127     bool measure = false;
128     bool clk = false;
129     uint32_t idx = 0;
130 
131     // If no arguments passed, bail out after dumping
132     // usage information.
133     if (argc == 1) {
134         return usage(cmd);
135     }
136 
137     // Parse all args.
138     while (argc > 1) {
139         const char* arg = argv[1];
140         if (prefix_match(&arg, "measure")) {
141             measure = true;
142         }
143         if (prefix_match(&arg, "-idx")) {
144             index = getValue(argc, argv, "-idx");
145             clk = true;
146             if (index) {
147                 idx = atoi(index);
148             } else {
149                 fprintf(stderr, "Enter Valid CLK IDX.\n");
150             }
151         }
152         if (prefix_match(&arg, "help")) {
153             return usage(cmd);
154         }
155         argc--;
156         argv++;
157     }
158 
159     // Get the device path.
160     path = guess_dev();
161     if (!path) {
162         fprintf(stderr, "No CLK device found.\n");
163         return usage(cmd);
164     }
165 
166     // Measure the clocks.
167     if (measure) {
168         err = measure_clk(path, idx, clk);
169         if (err) {
170             printf("Measure CLK failed.\n");
171         }
172     }
173     return 0;
174 }
175