1 // Copyright 2016 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 <ddk/debug.h>
6 #include <zircon/device/device.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 
usage(void)13 static void usage(void) {
14     fprintf(stderr,
15             "Usage: driverctl <path> <command> [options]\n"
16             "\n"
17             "where path is path to driver file in /dev\n"
18             "\n"
19             "Command \"log\":\n"
20             "  options are zero or more of:\n"
21             "    \"error\" or \"e\":   DDK_LOG_ERROR\n"
22             "    \"warn\" or \"w\":    DDK_LOG_WARN\n"
23             "    \"info\" or \"i\":    DDK_LOG_INFO\n"
24             "    \"trace\" or \"t\":   DDK_LOG_TRACE\n"
25             "    \"spew\" or \"s\":    DDK_LOG_SPEW\n"
26             "    \"debug1\" or \"d1\": DDK_LOG_DEBUG1\n"
27             "    \"debug2\" or \"d2\": DDK_LOG_DEBUG2\n"
28             "    \"debug3\" or \"d3\": DDK_LOG_DEBUG3\n"
29             "    \"debug4\" or \"d4\": DDK_LOG_DEBUG4\n"
30             "\n"
31             "  With no options provided, driverctl log will print the current log flags for the driver.\n"
32             "  A flag may have a '+' or '-' prepended. In that case the flag will be toggled\n"
33             "  on (+) or off(-) without affecting other flags.\n"
34             "  If toggled flags are used, all flags must be toggled.\n"
35             "\n"
36             "  Examples:\n"
37             "\n"
38             "  Set log flags to DDK_LOG_ERROR | DDK_LOG_INFO | DDK_LOG_TRACE:\n"
39             "    $ driverctl <path> log error info trace\n"
40             "  or:\n"
41             "    $ driverctl <path> log e i t\n"
42              "\n"
43             "  Turn on DDK_LOG_TRACE and DDK_LOG_SPEW:\n"
44             "    $ driverctl <path> log +trace +spew\n"
45             "  or:\n"
46             "    $ driverctl <path> log +t +s\n"
47              "\n"
48             "  Turn off DDK_LOG_SPEW:\n"
49             "    $ driverctl <path> log -spew\n"
50             "  or:\n"
51             "    $ driverctl <path> log -s\n"
52             );
53 }
54 
main(int argc,char ** argv)55 int main(int argc, char **argv) {
56     int ret = 0;
57 
58     if (argc < 3) {
59         usage();
60         return -1;
61     }
62 
63     const char* path = argv[1];
64     if (!strcmp(path, "-h")) {
65         usage();
66         return 0;
67     }
68 
69     const char* command = argv[2];
70     if (strcmp(command, "log")) {
71         fprintf(stderr, "Unsupported command %s\n", command);
72         usage();
73         return -1;
74     }
75 
76     int fd = open(path, O_RDWR);
77     if (fd < 0) {
78         fprintf(stderr, "could not open %s\n", path);
79         return -1;
80     }
81 
82     if (argc == 3) {
83         uint32_t flags;
84         ret = ioctl_device_get_log_flags(fd, &flags);
85         if (ret < 0) {
86             fprintf(stderr, "ioctl_device_get_log_flags failed for %s\n", path);
87         } else {
88             printf("Log flags:");
89             if (flags & DDK_LOG_ERROR) {
90                 printf(" ERROR");
91             }
92             if (flags & DDK_LOG_WARN) {
93                 printf(" WARN");
94             }
95             if (flags & DDK_LOG_INFO) {
96                 printf(" INFO");
97             }
98             if (flags & DDK_LOG_TRACE) {
99                 printf(" TRACE");
100             }
101             if (flags & DDK_LOG_SPEW) {
102                 printf(" SPEW");
103             }
104             if (flags & DDK_LOG_DEBUG1) {
105                 printf(" DEBUG1");
106             }
107             if (flags & DDK_LOG_DEBUG2) {
108                 printf(" DEBUG2");
109             }
110             if (flags & DDK_LOG_DEBUG3) {
111                 printf(" DEBUG3");
112             }
113             if (flags & DDK_LOG_DEBUG4) {
114                 printf(" DEBUG4");
115             }
116             printf("\n");
117         }
118         goto out;
119     }
120 
121     driver_log_flags_t flags = {0, 0};
122     char* toggle_arg = NULL;
123     char* non_toggle_arg = NULL;
124 
125     for (int i = 3; i < argc; i++) {
126         char* arg = argv[i];
127         char toggle = arg[0];
128         uint32_t flag = 0;
129 
130         // check for leading + or -
131         if (toggle == '+' || toggle == '-') {
132             toggle_arg = arg;
133             arg++;
134         } else {
135             non_toggle_arg = arg;
136         }
137 
138         if (toggle_arg && non_toggle_arg) {
139             fprintf(stderr, "Cannot mix toggled flag \"%s\" with non-toggle flag \"%s\"\n",
140                     toggle_arg, non_toggle_arg);
141             usage();
142             ret = -1;
143             goto out;
144         }
145 
146         if (!strcasecmp(arg, "e") || !strcasecmp(arg, "error")) {
147             flag = DDK_LOG_ERROR;
148         } else if (!strcasecmp(arg, "w") || !strcasecmp(arg, "warn")) {
149             flag = DDK_LOG_WARN;
150         } else if (!strcasecmp(arg, "i") || !strcasecmp(arg, "info")) {
151             flag = DDK_LOG_INFO;
152         } else if (!strcasecmp(arg, "t") || !strcasecmp(arg, "trace")) {
153             flag = DDK_LOG_TRACE;
154         } else if (!strcasecmp(arg, "s") || !strcasecmp(arg, "spew")) {
155             flag = DDK_LOG_SPEW;
156         } else if (!strcasecmp(arg, "d1") || !strcasecmp(arg, "debug1")) {
157             flag = DDK_LOG_DEBUG1;
158         } else if (!strcasecmp(arg, "d2") || !strcasecmp(arg, "debug2")) {
159             flag = DDK_LOG_DEBUG2;
160         } else if (!strcasecmp(arg, "d3") || !strcasecmp(arg, "debug3")) {
161             flag = DDK_LOG_DEBUG3;
162         } else if (!strcasecmp(arg, "d4") || !strcasecmp(arg, "debug4")) {
163             flag = DDK_LOG_DEBUG4;
164         } else {
165             fprintf(stderr, "unknown flag %s\n", arg);
166             ret = -1;
167             goto out;
168         }
169 
170         if (toggle == '+') {
171             flags.set |= flag;
172         } else if (toggle == '-') {
173             flags.clear |= flag;
174         } else {
175             flags.set |= flag;
176         }
177     }
178 
179     if (!toggle_arg) {
180         // clear all flags not explicitly set if we aren't using flag toggles
181         flags.clear = ~flags.set;
182     }
183 
184     ret = ioctl_device_set_log_flags(fd, &flags);
185     if (ret < 0) {
186         fprintf(stderr, "ioctl_device_set_log_flags failed for %s\n", path);
187     }
188 
189 out:
190     close(fd);
191     return ret;
192 }
193