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