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