1/*-
2 * Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD$");
28
29#include <sys/linker_set.h>
30#include <devctl.h>
31#include <err.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <strings.h>
37#include <unistd.h>
38
39struct devctl_command {
40	const char *name;
41	int (*handler)(int ac, char **av);
42};
43
44#define	DEVCTL_DATASET(name)	devctl_ ## name ## _table
45
46#define	DEVCTL_COMMAND(set, name, function)				\
47	static struct devctl_command function ## _devctl_command =	\
48	{ #name, function };						\
49	DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command)
50
51#define	DEVCTL_TABLE(set, name)						\
52	SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command);	\
53									\
54	static int							\
55	devctl_ ## name ## _table_handler(int ac, char **av)		\
56	{								\
57		return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \
58		    SET_LIMIT(DEVCTL_DATASET(name)), ac, av));		\
59	}								\
60	DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler)
61
62static int	devctl_table_handler(struct devctl_command **start,
63    struct devctl_command **end, int ac, char **av);
64
65SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command);
66
67DEVCTL_TABLE(top, clear);
68DEVCTL_TABLE(top, set);
69
70static void
71usage(void)
72{
73	fprintf(stderr,
74	    "usage: devctl attach device\n"
75	    "       devctl detach [-f] device\n"
76	    "       devctl disable [-f] device\n"
77	    "       devctl enable device\n"
78	    "       devctl suspend device\n"
79	    "       devctl resume device\n"
80	    "       devctl set driver [-f] device driver\n"
81	    "       devctl clear driver [-f] device\n"
82	    "       devctl rescan device\n"
83	    "       devctl delete [-f] device\n"
84	    "       devctl freeze\n"
85	    "       devctl thaw\n"
86	    "       devctl reset [-d] device\n"
87	    );
88	exit(1);
89}
90
91static int
92devctl_table_handler(struct devctl_command **start,
93    struct devctl_command **end, int ac, char **av)
94{
95	struct devctl_command **cmd;
96
97	if (ac < 2) {
98		warnx("The %s command requires a sub-command.", av[0]);
99		return (EINVAL);
100	}
101	for (cmd = start; cmd < end; cmd++) {
102		if (strcmp((*cmd)->name, av[1]) == 0)
103			return ((*cmd)->handler(ac - 1, av + 1));
104	}
105
106	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
107	return (ENOENT);
108}
109
110static int
111help(int ac __unused, char **av __unused)
112{
113
114	usage();
115	return (0);
116}
117DEVCTL_COMMAND(top, help, help);
118
119static int
120attach(int ac, char **av)
121{
122
123	if (ac != 2)
124		usage();
125	if (devctl_attach(av[1]) < 0)
126		err(1, "Failed to attach %s", av[1]);
127	return (0);
128}
129DEVCTL_COMMAND(top, attach, attach);
130
131static void
132detach_usage(void)
133{
134
135	fprintf(stderr, "usage: devctl detach [-f] device\n");
136	exit(1);
137}
138
139static int
140detach(int ac, char **av)
141{
142	bool force;
143	int ch;
144
145	force = false;
146	while ((ch = getopt(ac, av, "f")) != -1)
147		switch (ch) {
148		case 'f':
149			force = true;
150			break;
151		default:
152			detach_usage();
153		}
154	ac -= optind;
155	av += optind;
156
157	if (ac != 1)
158		detach_usage();
159	if (devctl_detach(av[0], force) < 0)
160		err(1, "Failed to detach %s", av[0]);
161	return (0);
162}
163DEVCTL_COMMAND(top, detach, detach);
164
165static void
166disable_usage(void)
167{
168
169	fprintf(stderr, "usage: devctl disable [-f] device\n");
170	exit(1);
171}
172
173static int
174disable(int ac, char **av)
175{
176	bool force;
177	int ch;
178
179	force = false;
180	while ((ch = getopt(ac, av, "f")) != -1)
181		switch (ch) {
182		case 'f':
183			force = true;
184			break;
185		default:
186			disable_usage();
187		}
188	ac -= optind;
189	av += optind;
190
191	if (ac != 1)
192		disable_usage();
193	if (devctl_disable(av[0], force) < 0)
194		err(1, "Failed to disable %s", av[0]);
195	return (0);
196}
197DEVCTL_COMMAND(top, disable, disable);
198
199static int
200enable(int ac, char **av)
201{
202
203	if (ac != 2)
204		usage();
205	if (devctl_enable(av[1]) < 0)
206		err(1, "Failed to enable %s", av[1]);
207	return (0);
208}
209DEVCTL_COMMAND(top, enable, enable);
210
211static int
212suspend(int ac, char **av)
213{
214
215	if (ac != 2)
216		usage();
217	if (devctl_suspend(av[1]) < 0)
218		err(1, "Failed to suspend %s", av[1]);
219	return (0);
220}
221DEVCTL_COMMAND(top, suspend, suspend);
222
223static int
224resume(int ac, char **av)
225{
226
227	if (ac != 2)
228		usage();
229	if (devctl_resume(av[1]) < 0)
230		err(1, "Failed to resume %s", av[1]);
231	return (0);
232}
233DEVCTL_COMMAND(top, resume, resume);
234
235static void
236set_driver_usage(void)
237{
238
239	fprintf(stderr, "usage: devctl set driver [-f] device driver\n");
240	exit(1);
241}
242
243static int
244set_driver(int ac, char **av)
245{
246	bool force;
247	int ch;
248
249	force = false;
250	while ((ch = getopt(ac, av, "f")) != -1)
251		switch (ch) {
252		case 'f':
253			force = true;
254			break;
255		default:
256			set_driver_usage();
257		}
258	ac -= optind;
259	av += optind;
260
261	if (ac != 2)
262		set_driver_usage();
263	if (devctl_set_driver(av[0], av[1], force) < 0)
264		err(1, "Failed to set %s driver to %s", av[0], av[1]);
265	return (0);
266}
267DEVCTL_COMMAND(set, driver, set_driver);
268
269static void
270clear_driver_usage(void)
271{
272
273	fprintf(stderr, "usage: devctl clear driver [-f] device\n");
274	exit(1);
275}
276
277static int
278clear_driver(int ac, char **av)
279{
280	bool force;
281	int ch;
282
283	force = false;
284	while ((ch = getopt(ac, av, "f")) != -1)
285		switch (ch) {
286		case 'f':
287			force = true;
288			break;
289		default:
290			clear_driver_usage();
291		}
292	ac -= optind;
293	av += optind;
294
295	if (ac != 1)
296		clear_driver_usage();
297	if (devctl_clear_driver(av[0], force) < 0)
298		err(1, "Failed to clear %s driver", av[0]);
299	return (0);
300}
301DEVCTL_COMMAND(clear, driver, clear_driver);
302
303static int
304rescan(int ac, char **av)
305{
306
307	if (ac != 2)
308		usage();
309	if (devctl_rescan(av[1]) < 0)
310		err(1, "Failed to rescan %s", av[1]);
311	return (0);
312}
313DEVCTL_COMMAND(top, rescan, rescan);
314
315static void
316delete_usage(void)
317{
318
319	fprintf(stderr, "usage: devctl delete [-f] device\n");
320	exit(1);
321}
322
323static int
324delete(int ac, char **av)
325{
326	bool force;
327	int ch;
328
329	force = false;
330	while ((ch = getopt(ac, av, "f")) != -1)
331		switch (ch) {
332		case 'f':
333			force = true;
334			break;
335		default:
336			delete_usage();
337		}
338	ac -= optind;
339	av += optind;
340
341	if (ac != 1)
342		delete_usage();
343	if (devctl_delete(av[0], force) < 0)
344		err(1, "Failed to delete %s", av[0]);
345	return (0);
346}
347DEVCTL_COMMAND(top, delete, delete);
348
349static void
350freeze_usage(void)
351{
352
353	fprintf(stderr, "usage: devctl freeze\n");
354	exit(1);
355}
356
357static int
358freeze(int ac, char **av __unused)
359{
360
361	if (ac != 1)
362		freeze_usage();
363	if (devctl_freeze() < 0)
364		err(1, "Failed to freeze probe/attach");
365	return (0);
366}
367DEVCTL_COMMAND(top, freeze, freeze);
368
369static void
370thaw_usage(void)
371{
372
373	fprintf(stderr, "usage: devctl thaw\n");
374	exit(1);
375}
376
377static int
378thaw(int ac, char **av __unused)
379{
380
381	if (ac != 1)
382		thaw_usage();
383	if (devctl_thaw() < 0)
384		err(1, "Failed to thaw probe/attach");
385	return (0);
386}
387DEVCTL_COMMAND(top, thaw, thaw);
388
389static void
390reset_usage(void)
391{
392
393	fprintf(stderr, "usage: devctl reset [-d] device\n");
394	exit(1);
395}
396
397static int
398reset(int ac, char **av)
399{
400	bool detach_drv;
401	int ch;
402
403	detach_drv = false;
404	while ((ch = getopt(ac, av, "d")) != -1)
405		switch (ch) {
406		case 'd':
407			detach_drv = true;
408			break;
409		default:
410			reset_usage();
411		}
412	ac -= optind;
413	av += optind;
414
415	if (ac != 1)
416		reset_usage();
417	if (devctl_reset(av[0], detach_drv) < 0)
418		err(1, "Failed to reset %s", av[0]);
419	return (0);
420}
421DEVCTL_COMMAND(top, reset, reset);
422
423int
424main(int ac, char *av[])
425{
426	struct devctl_command **cmd;
427
428	if (ac == 1)
429		usage();
430	ac--;
431	av++;
432
433	SET_FOREACH(cmd, DEVCTL_DATASET(top)) {
434		if (strcmp((*cmd)->name, av[0]) == 0) {
435			if ((*cmd)->handler(ac, av) != 0)
436				return (1);
437			else
438				return (0);
439		}
440	}
441	warnx("Unknown command %s.", av[0]);
442	return (1);
443}
444