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