1278320Sjhb/*-
2278320Sjhb * Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
3278320Sjhb *
4278320Sjhb * Redistribution and use in source and binary forms, with or without
5278320Sjhb * modification, are permitted provided that the following conditions
6278320Sjhb * are met:
7278320Sjhb * 1. Redistributions of source code must retain the above copyright
8278320Sjhb *    notice, this list of conditions and the following disclaimer.
9278320Sjhb * 2. Redistributions in binary form must reproduce the above copyright
10278320Sjhb *    notice, this list of conditions and the following disclaimer in the
11278320Sjhb *    documentation and/or other materials provided with the distribution.
12278320Sjhb *
13278320Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14278320Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15278320Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16278320Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17278320Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18278320Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19278320Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20278320Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21278320Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22278320Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23278320Sjhb * SUCH DAMAGE.
24278320Sjhb */
25278320Sjhb
26278320Sjhb#include <sys/cdefs.h>
27278320Sjhb__FBSDID("$FreeBSD: stable/11/usr.sbin/devctl/devctl.c 367457 2020-11-07 18:10:59Z dim $");
28278320Sjhb
29278320Sjhb#include <sys/linker_set.h>
30278320Sjhb#include <devctl.h>
31278320Sjhb#include <err.h>
32278320Sjhb#include <errno.h>
33278320Sjhb#include <stdio.h>
34278320Sjhb#include <stdlib.h>
35278320Sjhb#include <string.h>
36278320Sjhb#include <strings.h>
37278320Sjhb#include <unistd.h>
38278320Sjhb
39278320Sjhbstruct devctl_command {
40278320Sjhb	const char *name;
41278320Sjhb	int (*handler)(int ac, char **av);
42278320Sjhb};
43278320Sjhb
44278320Sjhb#define	DEVCTL_DATASET(name)	devctl_ ## name ## _table
45278320Sjhb
46278320Sjhb#define	DEVCTL_COMMAND(set, name, function)				\
47278320Sjhb	static struct devctl_command function ## _devctl_command =	\
48278320Sjhb	{ #name, function };						\
49278320Sjhb	DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command)
50278320Sjhb
51278320Sjhb#define	DEVCTL_TABLE(set, name)						\
52278320Sjhb	SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command);	\
53278320Sjhb									\
54278320Sjhb	static int							\
55278320Sjhb	devctl_ ## name ## _table_handler(int ac, char **av)		\
56278320Sjhb	{								\
57278320Sjhb		return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \
58278320Sjhb		    SET_LIMIT(DEVCTL_DATASET(name)), ac, av));		\
59278320Sjhb	}								\
60278320Sjhb	DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler)
61278320Sjhb
62278320Sjhbstatic int	devctl_table_handler(struct devctl_command **start,
63278320Sjhb    struct devctl_command **end, int ac, char **av);
64278320Sjhb
65278320SjhbSET_DECLARE(DEVCTL_DATASET(top), struct devctl_command);
66278320Sjhb
67306533SjhbDEVCTL_TABLE(top, clear);
68278320SjhbDEVCTL_TABLE(top, set);
69278320Sjhb
70278320Sjhbstatic void
71278320Sjhbusage(void)
72278320Sjhb{
73346384Skib	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
74278320Sjhb	    "usage: devctl attach device",
75278320Sjhb	    "       devctl detach [-f] device",
76278320Sjhb	    "       devctl disable [-f] device",
77278320Sjhb	    "       devctl enable device",
78278320Sjhb	    "       devctl suspend device",
79278320Sjhb	    "       devctl resume device",
80298709Sjhb	    "       devctl set driver [-f] device driver",
81306533Sjhb	    "       devctl clear driver [-f] device",
82298709Sjhb	    "       devctl rescan device",
83346384Skib	    "       devctl delete [-f] device",
84346384Skib	    "       devctl reset [-d] device"
85346384Skib	    );
86278320Sjhb	exit(1);
87278320Sjhb}
88278320Sjhb
89278320Sjhbstatic int
90278320Sjhbdevctl_table_handler(struct devctl_command **start,
91278320Sjhb    struct devctl_command **end, int ac, char **av)
92278320Sjhb{
93278320Sjhb	struct devctl_command **cmd;
94278320Sjhb
95278320Sjhb	if (ac < 2) {
96278320Sjhb		warnx("The %s command requires a sub-command.", av[0]);
97278320Sjhb		return (EINVAL);
98278320Sjhb	}
99278320Sjhb	for (cmd = start; cmd < end; cmd++) {
100278320Sjhb		if (strcmp((*cmd)->name, av[1]) == 0)
101278320Sjhb			return ((*cmd)->handler(ac - 1, av + 1));
102278320Sjhb	}
103278320Sjhb
104278320Sjhb	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
105278320Sjhb	return (ENOENT);
106278320Sjhb}
107278320Sjhb
108278320Sjhbstatic int
109278320Sjhbhelp(int ac __unused, char **av __unused)
110278320Sjhb{
111278320Sjhb
112278320Sjhb	usage();
113278320Sjhb	return (0);
114278320Sjhb}
115278320SjhbDEVCTL_COMMAND(top, help, help);
116278320Sjhb
117278320Sjhbstatic int
118278320Sjhbattach(int ac, char **av)
119278320Sjhb{
120278320Sjhb
121278320Sjhb	if (ac != 2)
122278320Sjhb		usage();
123278320Sjhb	if (devctl_attach(av[1]) < 0)
124278320Sjhb		err(1, "Failed to attach %s", av[1]);
125278320Sjhb	return (0);
126278320Sjhb}
127278320SjhbDEVCTL_COMMAND(top, attach, attach);
128278320Sjhb
129278320Sjhbstatic void
130278320Sjhbdetach_usage(void)
131278320Sjhb{
132278320Sjhb
133278320Sjhb	fprintf(stderr, "usage: devctl detach [-f] device\n");
134278320Sjhb	exit(1);
135278320Sjhb}
136278320Sjhb
137278320Sjhbstatic int
138278320Sjhbdetach(int ac, char **av)
139278320Sjhb{
140278320Sjhb	bool force;
141278320Sjhb	int ch;
142278320Sjhb
143278320Sjhb	force = false;
144278320Sjhb	while ((ch = getopt(ac, av, "f")) != -1)
145278320Sjhb		switch (ch) {
146278320Sjhb		case 'f':
147278320Sjhb			force = true;
148278320Sjhb			break;
149278320Sjhb		default:
150278320Sjhb			detach_usage();
151278320Sjhb		}
152278320Sjhb	ac -= optind;
153278320Sjhb	av += optind;
154278320Sjhb
155278320Sjhb	if (ac != 1)
156278320Sjhb		detach_usage();
157278320Sjhb	if (devctl_detach(av[0], force) < 0)
158278320Sjhb		err(1, "Failed to detach %s", av[0]);
159278320Sjhb	return (0);
160278320Sjhb}
161278320SjhbDEVCTL_COMMAND(top, detach, detach);
162278320Sjhb
163278320Sjhbstatic void
164278320Sjhbdisable_usage(void)
165278320Sjhb{
166278320Sjhb
167278320Sjhb	fprintf(stderr, "usage: devctl disable [-f] device\n");
168278320Sjhb	exit(1);
169278320Sjhb}
170278320Sjhb
171278320Sjhbstatic int
172278320Sjhbdisable(int ac, char **av)
173278320Sjhb{
174278320Sjhb	bool force;
175278320Sjhb	int ch;
176278320Sjhb
177278320Sjhb	force = false;
178278320Sjhb	while ((ch = getopt(ac, av, "f")) != -1)
179278320Sjhb		switch (ch) {
180278320Sjhb		case 'f':
181278320Sjhb			force = true;
182278320Sjhb			break;
183278320Sjhb		default:
184278320Sjhb			disable_usage();
185278320Sjhb		}
186278320Sjhb	ac -= optind;
187278320Sjhb	av += optind;
188278320Sjhb
189278320Sjhb	if (ac != 1)
190278320Sjhb		disable_usage();
191278320Sjhb	if (devctl_disable(av[0], force) < 0)
192278320Sjhb		err(1, "Failed to disable %s", av[0]);
193278320Sjhb	return (0);
194278320Sjhb}
195278320SjhbDEVCTL_COMMAND(top, disable, disable);
196278320Sjhb
197278320Sjhbstatic int
198278320Sjhbenable(int ac, char **av)
199278320Sjhb{
200278320Sjhb
201278320Sjhb	if (ac != 2)
202278320Sjhb		usage();
203278320Sjhb	if (devctl_enable(av[1]) < 0)
204278320Sjhb		err(1, "Failed to enable %s", av[1]);
205278320Sjhb	return (0);
206278320Sjhb}
207278320SjhbDEVCTL_COMMAND(top, enable, enable);
208278320Sjhb
209278320Sjhbstatic int
210278320Sjhbsuspend(int ac, char **av)
211278320Sjhb{
212278320Sjhb
213278320Sjhb	if (ac != 2)
214278320Sjhb		usage();
215278320Sjhb	if (devctl_suspend(av[1]) < 0)
216278320Sjhb		err(1, "Failed to suspend %s", av[1]);
217278320Sjhb	return (0);
218278320Sjhb}
219278320SjhbDEVCTL_COMMAND(top, suspend, suspend);
220278320Sjhb
221278320Sjhbstatic int
222278320Sjhbresume(int ac, char **av)
223278320Sjhb{
224278320Sjhb
225278320Sjhb	if (ac != 2)
226278320Sjhb		usage();
227278320Sjhb	if (devctl_resume(av[1]) < 0)
228278320Sjhb		err(1, "Failed to resume %s", av[1]);
229278320Sjhb	return (0);
230278320Sjhb}
231278320SjhbDEVCTL_COMMAND(top, resume, resume);
232278320Sjhb
233278320Sjhbstatic void
234278320Sjhbset_driver_usage(void)
235278320Sjhb{
236278320Sjhb
237278320Sjhb	fprintf(stderr, "usage: devctl set driver [-f] device driver\n");
238278320Sjhb	exit(1);
239278320Sjhb}
240278320Sjhb
241278320Sjhbstatic int
242278320Sjhbset_driver(int ac, char **av)
243278320Sjhb{
244278320Sjhb	bool force;
245278320Sjhb	int ch;
246278320Sjhb
247278320Sjhb	force = false;
248278320Sjhb	while ((ch = getopt(ac, av, "f")) != -1)
249278320Sjhb		switch (ch) {
250278320Sjhb		case 'f':
251278320Sjhb			force = true;
252278320Sjhb			break;
253278320Sjhb		default:
254278320Sjhb			set_driver_usage();
255278320Sjhb		}
256278320Sjhb	ac -= optind;
257278320Sjhb	av += optind;
258278320Sjhb
259278320Sjhb	if (ac != 2)
260278320Sjhb		set_driver_usage();
261278320Sjhb	if (devctl_set_driver(av[0], av[1], force) < 0)
262278320Sjhb		err(1, "Failed to set %s driver to %s", av[0], av[1]);
263278320Sjhb	return (0);
264278320Sjhb}
265278320SjhbDEVCTL_COMMAND(set, driver, set_driver);
266278320Sjhb
267306533Sjhbstatic void
268306533Sjhbclear_driver_usage(void)
269306533Sjhb{
270306533Sjhb
271306533Sjhb	fprintf(stderr, "usage: devctl clear driver [-f] device\n");
272306533Sjhb	exit(1);
273306533Sjhb}
274306533Sjhb
275298707Sjhbstatic int
276306533Sjhbclear_driver(int ac, char **av)
277306533Sjhb{
278306533Sjhb	bool force;
279306533Sjhb	int ch;
280306533Sjhb
281306533Sjhb	force = false;
282306533Sjhb	while ((ch = getopt(ac, av, "f")) != -1)
283306533Sjhb		switch (ch) {
284306533Sjhb		case 'f':
285306533Sjhb			force = true;
286306533Sjhb			break;
287306533Sjhb		default:
288306533Sjhb			clear_driver_usage();
289306533Sjhb		}
290306533Sjhb	ac -= optind;
291306533Sjhb	av += optind;
292306533Sjhb
293306533Sjhb	if (ac != 1)
294306533Sjhb		clear_driver_usage();
295306533Sjhb	if (devctl_clear_driver(av[0], force) < 0)
296306533Sjhb		err(1, "Failed to clear %s driver", av[0]);
297306533Sjhb	return (0);
298306533Sjhb}
299306533SjhbDEVCTL_COMMAND(clear, driver, clear_driver);
300306533Sjhb
301306533Sjhbstatic int
302298707Sjhbrescan(int ac, char **av)
303298707Sjhb{
304298707Sjhb
305298707Sjhb	if (ac != 2)
306298707Sjhb		usage();
307298707Sjhb	if (devctl_rescan(av[1]) < 0)
308298707Sjhb		err(1, "Failed to rescan %s", av[1]);
309298707Sjhb	return (0);
310298707Sjhb}
311298707SjhbDEVCTL_COMMAND(top, rescan, rescan);
312298707Sjhb
313298709Sjhbstatic void
314298709Sjhbdelete_usage(void)
315298709Sjhb{
316298709Sjhb
317298709Sjhb	fprintf(stderr, "usage: devctl delete [-f] device\n");
318298709Sjhb	exit(1);
319298709Sjhb}
320298709Sjhb
321298709Sjhbstatic int
322298709Sjhbdelete(int ac, char **av)
323298709Sjhb{
324298709Sjhb	bool force;
325298709Sjhb	int ch;
326298709Sjhb
327298709Sjhb	force = false;
328298709Sjhb	while ((ch = getopt(ac, av, "f")) != -1)
329298709Sjhb		switch (ch) {
330298709Sjhb		case 'f':
331298709Sjhb			force = true;
332298709Sjhb			break;
333298709Sjhb		default:
334298709Sjhb			delete_usage();
335298709Sjhb		}
336298709Sjhb	ac -= optind;
337298709Sjhb	av += optind;
338298709Sjhb
339298709Sjhb	if (ac != 1)
340298709Sjhb		delete_usage();
341298709Sjhb	if (devctl_delete(av[0], force) < 0)
342298709Sjhb		err(1, "Failed to delete %s", av[0]);
343298709Sjhb	return (0);
344298709Sjhb}
345298709SjhbDEVCTL_COMMAND(top, delete, delete);
346298709Sjhb
347346384Skibstatic void
348346384Skibreset_usage(void)
349346384Skib{
350346384Skib
351346384Skib	fprintf(stderr, "usage: devctl reset [-d] device\n");
352346384Skib	exit(1);
353346384Skib}
354346384Skib
355346384Skibstatic int
356346384Skibreset(int ac, char **av)
357346384Skib{
358346384Skib	bool detach_drv;
359346384Skib	int ch;
360346384Skib
361346384Skib	detach_drv = false;
362346384Skib	while ((ch = getopt(ac, av, "d")) != -1)
363346384Skib		switch (ch) {
364346384Skib		case 'd':
365346384Skib			detach_drv = true;
366346384Skib			break;
367346384Skib		default:
368346384Skib			reset_usage();
369346384Skib		}
370346384Skib	ac -= optind;
371346384Skib	av += optind;
372346384Skib
373346384Skib	if (ac != 1)
374346384Skib		reset_usage();
375346384Skib	if (devctl_reset(av[0], detach_drv) < 0)
376346384Skib		err(1, "Failed to reset %s", av[0]);
377346384Skib	return (0);
378346384Skib}
379346384SkibDEVCTL_COMMAND(top, reset, reset);
380346384Skib
381278320Sjhbint
382278320Sjhbmain(int ac, char *av[])
383278320Sjhb{
384278320Sjhb	struct devctl_command **cmd;
385278320Sjhb
386278320Sjhb	if (ac == 1)
387278320Sjhb		usage();
388278320Sjhb	ac--;
389278320Sjhb	av++;
390278320Sjhb
391278320Sjhb	SET_FOREACH(cmd, DEVCTL_DATASET(top)) {
392278320Sjhb		if (strcmp((*cmd)->name, av[0]) == 0) {
393278320Sjhb			if ((*cmd)->handler(ac, av) != 0)
394278320Sjhb				return (1);
395278320Sjhb			else
396278320Sjhb				return (0);
397278320Sjhb		}
398278320Sjhb	}
399278320Sjhb	warnx("Unknown command %s.", av[0]);
400278320Sjhb	return (1);
401278320Sjhb}
402