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#include <sys/linker_set.h>
28#include <devctl.h>
29#include <err.h>
30#include <errno.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <strings.h>
35#include <unistd.h>
36
37struct devctl_command {
38	const char *name;
39	int (*handler)(int ac, char **av);
40};
41
42#define	DEVCTL_DATASET(name)	devctl_ ## name ## _table
43
44#define	DEVCTL_COMMAND(set, name, function)				\
45	static struct devctl_command function ## _devctl_command =	\
46	{ #name, function };						\
47	DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command)
48
49#define	DEVCTL_TABLE(set, name)						\
50	SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command);	\
51									\
52	static int							\
53	devctl_ ## name ## _table_handler(int ac, char **av)		\
54	{								\
55		return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \
56		    SET_LIMIT(DEVCTL_DATASET(name)), ac, av));		\
57	}								\
58	DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler)
59
60static int	devctl_table_handler(struct devctl_command **start,
61    struct devctl_command **end, int ac, char **av);
62
63SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command);
64
65DEVCTL_TABLE(top, clear);
66DEVCTL_TABLE(top, set);
67
68static void
69usage(void)
70{
71	fprintf(stderr,
72	    "usage: devctl attach device\n"
73	    "       devctl detach [-f] device\n"
74	    "       devctl disable [-f] device\n"
75	    "       devctl enable device\n"
76	    "       devctl suspend device\n"
77	    "       devctl resume device\n"
78	    "       devctl set driver [-f] device driver\n"
79	    "       devctl clear driver [-f] device\n"
80	    "       devctl rescan device\n"
81	    "       devctl delete [-f] device\n"
82	    "       devctl freeze\n"
83	    "       devctl thaw\n"
84	    "       devctl reset [-d] device\n"
85	    "       devctl getpath locator device\n"
86	    );
87	exit(1);
88}
89
90static int
91devctl_table_handler(struct devctl_command **start,
92    struct devctl_command **end, int ac, char **av)
93{
94	struct devctl_command **cmd;
95
96	if (ac < 2) {
97		warnx("The %s command requires a sub-command.", av[0]);
98		return (EINVAL);
99	}
100	for (cmd = start; cmd < end; cmd++) {
101		if (strcmp((*cmd)->name, av[1]) == 0)
102			return ((*cmd)->handler(ac - 1, av + 1));
103	}
104
105	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
106	return (ENOENT);
107}
108
109static int
110help(int ac __unused, char **av __unused)
111{
112
113	usage();
114	return (0);
115}
116DEVCTL_COMMAND(top, help, help);
117
118static int
119attach(int ac, char **av)
120{
121
122	if (ac != 2)
123		usage();
124	if (devctl_attach(av[1]) < 0)
125		err(1, "Failed to attach %s", av[1]);
126	return (0);
127}
128DEVCTL_COMMAND(top, attach, attach);
129
130static void
131detach_usage(void)
132{
133
134	fprintf(stderr, "usage: devctl detach [-f] device\n");
135	exit(1);
136}
137
138static int
139detach(int ac, char **av)
140{
141	bool force;
142	int ch;
143
144	force = false;
145	while ((ch = getopt(ac, av, "f")) != -1)
146		switch (ch) {
147		case 'f':
148			force = true;
149			break;
150		default:
151			detach_usage();
152		}
153	ac -= optind;
154	av += optind;
155
156	if (ac != 1)
157		detach_usage();
158	if (devctl_detach(av[0], force) < 0)
159		err(1, "Failed to detach %s", av[0]);
160	return (0);
161}
162DEVCTL_COMMAND(top, detach, detach);
163
164static void
165disable_usage(void)
166{
167
168	fprintf(stderr, "usage: devctl disable [-f] device\n");
169	exit(1);
170}
171
172static int
173disable(int ac, char **av)
174{
175	bool force;
176	int ch;
177
178	force = false;
179	while ((ch = getopt(ac, av, "f")) != -1)
180		switch (ch) {
181		case 'f':
182			force = true;
183			break;
184		default:
185			disable_usage();
186		}
187	ac -= optind;
188	av += optind;
189
190	if (ac != 1)
191		disable_usage();
192	if (devctl_disable(av[0], force) < 0)
193		err(1, "Failed to disable %s", av[0]);
194	return (0);
195}
196DEVCTL_COMMAND(top, disable, disable);
197
198static int
199enable(int ac, char **av)
200{
201
202	if (ac != 2)
203		usage();
204	if (devctl_enable(av[1]) < 0)
205		err(1, "Failed to enable %s", av[1]);
206	return (0);
207}
208DEVCTL_COMMAND(top, enable, enable);
209
210static int
211suspend(int ac, char **av)
212{
213
214	if (ac != 2)
215		usage();
216	if (devctl_suspend(av[1]) < 0)
217		err(1, "Failed to suspend %s", av[1]);
218	return (0);
219}
220DEVCTL_COMMAND(top, suspend, suspend);
221
222static int
223resume(int ac, char **av)
224{
225
226	if (ac != 2)
227		usage();
228	if (devctl_resume(av[1]) < 0)
229		err(1, "Failed to resume %s", av[1]);
230	return (0);
231}
232DEVCTL_COMMAND(top, resume, resume);
233
234static void
235set_driver_usage(void)
236{
237
238	fprintf(stderr, "usage: devctl set driver [-f] device driver\n");
239	exit(1);
240}
241
242static int
243set_driver(int ac, char **av)
244{
245	bool force;
246	int ch;
247
248	force = false;
249	while ((ch = getopt(ac, av, "f")) != -1)
250		switch (ch) {
251		case 'f':
252			force = true;
253			break;
254		default:
255			set_driver_usage();
256		}
257	ac -= optind;
258	av += optind;
259
260	if (ac != 2)
261		set_driver_usage();
262	if (devctl_set_driver(av[0], av[1], force) < 0)
263		err(1, "Failed to set %s driver to %s", av[0], av[1]);
264	return (0);
265}
266DEVCTL_COMMAND(set, driver, set_driver);
267
268static void
269clear_driver_usage(void)
270{
271
272	fprintf(stderr, "usage: devctl clear driver [-f] device\n");
273	exit(1);
274}
275
276static int
277clear_driver(int ac, char **av)
278{
279	bool force;
280	int ch;
281
282	force = false;
283	while ((ch = getopt(ac, av, "f")) != -1)
284		switch (ch) {
285		case 'f':
286			force = true;
287			break;
288		default:
289			clear_driver_usage();
290		}
291	ac -= optind;
292	av += optind;
293
294	if (ac != 1)
295		clear_driver_usage();
296	if (devctl_clear_driver(av[0], force) < 0)
297		err(1, "Failed to clear %s driver", av[0]);
298	return (0);
299}
300DEVCTL_COMMAND(clear, driver, clear_driver);
301
302static int
303rescan(int ac, char **av)
304{
305
306	if (ac != 2)
307		usage();
308	if (devctl_rescan(av[1]) < 0)
309		err(1, "Failed to rescan %s", av[1]);
310	return (0);
311}
312DEVCTL_COMMAND(top, rescan, rescan);
313
314static void
315delete_usage(void)
316{
317
318	fprintf(stderr, "usage: devctl delete [-f] device\n");
319	exit(1);
320}
321
322static int
323delete(int ac, char **av)
324{
325	bool force;
326	int ch;
327
328	force = false;
329	while ((ch = getopt(ac, av, "f")) != -1)
330		switch (ch) {
331		case 'f':
332			force = true;
333			break;
334		default:
335			delete_usage();
336		}
337	ac -= optind;
338	av += optind;
339
340	if (ac != 1)
341		delete_usage();
342	if (devctl_delete(av[0], force) < 0)
343		err(1, "Failed to delete %s", av[0]);
344	return (0);
345}
346DEVCTL_COMMAND(top, delete, delete);
347
348static void
349freeze_usage(void)
350{
351
352	fprintf(stderr, "usage: devctl freeze\n");
353	exit(1);
354}
355
356static int
357freeze(int ac, char **av __unused)
358{
359
360	if (ac != 1)
361		freeze_usage();
362	if (devctl_freeze() < 0)
363		err(1, "Failed to freeze probe/attach");
364	return (0);
365}
366DEVCTL_COMMAND(top, freeze, freeze);
367
368static void
369thaw_usage(void)
370{
371
372	fprintf(stderr, "usage: devctl thaw\n");
373	exit(1);
374}
375
376static int
377thaw(int ac, char **av __unused)
378{
379
380	if (ac != 1)
381		thaw_usage();
382	if (devctl_thaw() < 0)
383		err(1, "Failed to thaw probe/attach");
384	return (0);
385}
386DEVCTL_COMMAND(top, thaw, thaw);
387
388static void
389reset_usage(void)
390{
391
392	fprintf(stderr, "usage: devctl reset [-d] device\n");
393	exit(1);
394}
395
396static int
397reset(int ac, char **av)
398{
399	bool detach_drv;
400	int ch;
401
402	detach_drv = false;
403	while ((ch = getopt(ac, av, "d")) != -1)
404		switch (ch) {
405		case 'd':
406			detach_drv = true;
407			break;
408		default:
409			reset_usage();
410		}
411	ac -= optind;
412	av += optind;
413
414	if (ac != 1)
415		reset_usage();
416	if (devctl_reset(av[0], detach_drv) < 0)
417		err(1, "Failed to reset %s", av[0]);
418	return (0);
419}
420DEVCTL_COMMAND(top, reset, reset);
421
422static int
423getpath(int ac, char **av)
424{
425	char *buffer = NULL;
426
427	if (ac != 3)
428		usage();
429	if (devctl_getpath(av[2], av[1], &buffer) < 0)
430		err(1, "Failed to get path via %s to %s", av[1], av[2]);
431	printf("%s\n", buffer);
432	free(buffer);
433	return (0);
434}
435DEVCTL_COMMAND(top, getpath, getpath);
436
437int
438main(int ac, char *av[])
439{
440	struct devctl_command **cmd;
441
442	if (ac == 1)
443		usage();
444	ac--;
445	av++;
446
447	SET_FOREACH(cmd, DEVCTL_DATASET(top)) {
448		if (strcmp((*cmd)->name, av[0]) == 0) {
449			if ((*cmd)->handler(ac, av) != 0)
450				return (1);
451			else
452				return (0);
453		}
454	}
455	warnx("Unknown command %s.", av[0]);
456	return (1);
457}
458