xref: /freebsd/lib/libdevctl/devctl.c (revision 0966fb1b74b38b31dc83e3cca48701ec796bf73a)
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/types.h>
30  #include <sys/bus.h>
31  #include <errno.h>
32  #include <fcntl.h>
33  #include <stdlib.h>
34  #include <string.h>
35  #include "devctl.h"
36  
37  static int
38  devctl_request(u_long cmd, struct devreq *req)
39  {
40  	static int devctl2_fd = -1;
41  
42  	if (devctl2_fd == -1) {
43  		devctl2_fd = open("/dev/devctl2", O_RDONLY);
44  		if (devctl2_fd == -1)
45  			return (-1);
46  	}
47  	return (ioctl(devctl2_fd, cmd, req));
48  }
49  
50  static int
51  devctl_simple_request(u_long cmd, const char *name, int flags)
52  {
53  	struct devreq req;
54  
55  	memset(&req, 0, sizeof(req));
56  	if (strlcpy(req.dr_name, name, sizeof(req.dr_name)) >=
57  	    sizeof(req.dr_name)) {
58  		errno = EINVAL;
59  		return (-1);
60  	}
61  	req.dr_flags = flags;
62  	return (devctl_request(cmd, &req));
63  }
64  
65  int
66  devctl_attach(const char *device)
67  {
68  
69  	return (devctl_simple_request(DEV_ATTACH, device, 0));
70  }
71  
72  int
73  devctl_detach(const char *device, bool force)
74  {
75  
76  	return (devctl_simple_request(DEV_DETACH, device, force ?
77  	    DEVF_FORCE_DETACH : 0));
78  }
79  
80  int
81  devctl_enable(const char *device)
82  {
83  
84  	return (devctl_simple_request(DEV_ENABLE, device, 0));
85  }
86  
87  int
88  devctl_disable(const char *device, bool force_detach)
89  {
90  
91  	return (devctl_simple_request(DEV_DISABLE, device, force_detach ?
92  	    DEVF_FORCE_DETACH : 0));
93  }
94  
95  int
96  devctl_suspend(const char *device)
97  {
98  
99  	return (devctl_simple_request(DEV_SUSPEND, device, 0));
100  }
101  
102  int
103  devctl_resume(const char *device)
104  {
105  
106  	return (devctl_simple_request(DEV_RESUME, device, 0));
107  }
108  
109  int
110  devctl_set_driver(const char *device, const char *driver, bool force)
111  {
112  	struct devreq req;
113  
114  	memset(&req, 0, sizeof(req));
115  	if (strlcpy(req.dr_name, device, sizeof(req.dr_name)) >=
116  	    sizeof(req.dr_name)) {
117  		errno = EINVAL;
118  		return (-1);
119  	}
120  	req.dr_data = __DECONST(char *, driver);
121  	if (force)
122  		req.dr_flags |= DEVF_SET_DRIVER_DETACH;
123  	return (devctl_request(DEV_SET_DRIVER, &req));
124  }
125  
126  int
127  devctl_clear_driver(const char *device, bool force)
128  {
129  
130  	return (devctl_simple_request(DEV_CLEAR_DRIVER, device, force ?
131  	    DEVF_CLEAR_DRIVER_DETACH : 0));
132  }
133  
134  int
135  devctl_rescan(const char *device)
136  {
137  
138  	return (devctl_simple_request(DEV_RESCAN, device, 0));
139  }
140  
141  int
142  devctl_delete(const char *device, bool force)
143  {
144  
145  	return (devctl_simple_request(DEV_DELETE, device, force ?
146  	    DEVF_FORCE_DELETE : 0));
147  }
148  
149  int
150  devctl_freeze(void)
151  {
152  
153  	return (devctl_simple_request(DEV_FREEZE, "", 0));
154  }
155  
156  int
157  devctl_thaw(void)
158  {
159  
160  	return (devctl_simple_request(DEV_THAW, "", 0));
161  }
162  
163  int
164  devctl_reset(const char *device, bool detach)
165  {
166  
167  	return (devctl_simple_request(DEV_RESET, device, detach ?
168  	    DEVF_RESET_DETACH : 0));
169  }
170  
171  #define BUFLEN 1024
172  
173  int
174  devctl_getpath(const char *device, const char *locator, char **buffer)
175  {
176  	struct devreq req;
177  	int serrno;
178  
179  	memset(&req, 0, sizeof(req));
180  	if (strlcpy(req.dr_name, device, sizeof(req.dr_name)) >=
181  	    sizeof(req.dr_name)) {
182  		errno = EINVAL;
183  		*buffer = NULL;
184  		return (-1);
185  	}
186  
187  	/*
188  	 * Maybe do the request twice. Once to get the length, and then again to
189  	 * get the string if BUFLEN bytes is insufficient.
190  	 */
191  	req.dr_flags = 0;
192  	req.dr_buffer.length = BUFLEN;
193  again:
194  	req.dr_buffer.buffer = malloc(req.dr_buffer.length);
195  	strlcpy(req.dr_buffer.buffer, locator, req.dr_buffer.length);
196  	if (devctl_request(DEV_GET_PATH, &req) == 0) {
197  		*buffer = req.dr_buffer.buffer;
198  		return (0);
199  	}
200  	if (errno == ENAMETOOLONG && req.dr_buffer.length != BUFLEN) {
201  		free(req.dr_buffer.buffer);
202  		goto again;
203  	}
204  	serrno = errno;
205  	free(req.dr_buffer.buffer);
206  	errno = serrno;
207  	*buffer = NULL;
208  	return (-1);
209  }
210