1 1.51 riastrad /* $NetBSD: kern_drvctl.c,v 1.51 2022/03/28 12:33:22 riastradh Exp $ */ 2 1.1 drochner 3 1.1 drochner /* 4 1.1 drochner * Copyright (c) 2004 5 1.1 drochner * Matthias Drochner. All rights reserved. 6 1.1 drochner * 7 1.1 drochner * Redistribution and use in source and binary forms, with or without 8 1.1 drochner * modification, are permitted provided that the following conditions 9 1.1 drochner * are met: 10 1.1 drochner * 1. Redistributions of source code must retain the above copyright 11 1.1 drochner * notice, this list of conditions, and the following disclaimer. 12 1.1 drochner * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 drochner * notice, this list of conditions and the following disclaimer in the 14 1.1 drochner * documentation and/or other materials provided with the distribution. 15 1.1 drochner * 16 1.1 drochner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 drochner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 drochner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 drochner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 drochner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 drochner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 drochner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 drochner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 drochner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 drochner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 drochner * SUCH DAMAGE. 27 1.1 drochner */ 28 1.1 drochner 29 1.1 drochner #include <sys/cdefs.h> 30 1.51 riastrad __KERNEL_RCSID(0, "$NetBSD: kern_drvctl.c,v 1.51 2022/03/28 12:33:22 riastradh Exp $"); 31 1.1 drochner 32 1.1 drochner #include <sys/param.h> 33 1.1 drochner #include <sys/systm.h> 34 1.1 drochner #include <sys/kernel.h> 35 1.1 drochner #include <sys/conf.h> 36 1.1 drochner #include <sys/device.h> 37 1.1 drochner #include <sys/event.h> 38 1.17 jmcneill #include <sys/kmem.h> 39 1.1 drochner #include <sys/ioctl.h> 40 1.5 thorpej #include <sys/fcntl.h> 41 1.17 jmcneill #include <sys/file.h> 42 1.17 jmcneill #include <sys/filedesc.h> 43 1.20 jmcneill #include <sys/select.h> 44 1.20 jmcneill #include <sys/poll.h> 45 1.1 drochner #include <sys/drvctlio.h> 46 1.17 jmcneill #include <sys/devmon.h> 47 1.25 christos #include <sys/stat.h> 48 1.26 christos #include <sys/kauth.h> 49 1.27 nonaka #include <sys/lwp.h> 50 1.40 pgoyette #include <sys/module.h> 51 1.1 drochner 52 1.39 christos #include "ioconf.h" 53 1.39 christos 54 1.17 jmcneill struct drvctl_event { 55 1.17 jmcneill TAILQ_ENTRY(drvctl_event) dce_link; 56 1.17 jmcneill prop_dictionary_t dce_event; 57 1.17 jmcneill }; 58 1.17 jmcneill 59 1.17 jmcneill TAILQ_HEAD(drvctl_queue, drvctl_event); 60 1.17 jmcneill 61 1.17 jmcneill static struct drvctl_queue drvctl_eventq; /* FIFO */ 62 1.17 jmcneill static kcondvar_t drvctl_cond; 63 1.17 jmcneill static kmutex_t drvctl_lock; 64 1.17 jmcneill static int drvctl_nopen = 0, drvctl_eventcnt = 0; 65 1.20 jmcneill static struct selinfo drvctl_rdsel; 66 1.17 jmcneill 67 1.17 jmcneill #define DRVCTL_EVENTQ_DEPTH 64 /* arbitrary queue limit */ 68 1.17 jmcneill 69 1.17 jmcneill dev_type_open(drvctlopen); 70 1.1 drochner 71 1.1 drochner const struct cdevsw drvctl_cdevsw = { 72 1.35 dholland .d_open = drvctlopen, 73 1.35 dholland .d_close = nullclose, 74 1.35 dholland .d_read = nullread, 75 1.35 dholland .d_write = nullwrite, 76 1.35 dholland .d_ioctl = noioctl, 77 1.35 dholland .d_stop = nostop, 78 1.35 dholland .d_tty = notty, 79 1.35 dholland .d_poll = nopoll, 80 1.35 dholland .d_mmap = nommap, 81 1.35 dholland .d_kqfilter = nokqfilter, 82 1.36 dholland .d_discard = nodiscard, 83 1.35 dholland .d_flag = D_OTHER 84 1.1 drochner }; 85 1.1 drochner 86 1.17 jmcneill static int drvctl_read(struct file *, off_t *, struct uio *, 87 1.17 jmcneill kauth_cred_t, int); 88 1.17 jmcneill static int drvctl_write(struct file *, off_t *, struct uio *, 89 1.17 jmcneill kauth_cred_t, int); 90 1.17 jmcneill static int drvctl_ioctl(struct file *, u_long, void *); 91 1.20 jmcneill static int drvctl_poll(struct file *, int); 92 1.25 christos static int drvctl_stat(struct file *, struct stat *); 93 1.17 jmcneill static int drvctl_close(struct file *); 94 1.17 jmcneill 95 1.17 jmcneill static const struct fileops drvctl_fileops = { 96 1.43 christos .fo_name = "drvctl", 97 1.23 ad .fo_read = drvctl_read, 98 1.23 ad .fo_write = drvctl_write, 99 1.23 ad .fo_ioctl = drvctl_ioctl, 100 1.23 ad .fo_fcntl = fnullop_fcntl, 101 1.23 ad .fo_poll = drvctl_poll, 102 1.25 christos .fo_stat = drvctl_stat, 103 1.23 ad .fo_close = drvctl_close, 104 1.23 ad .fo_kqfilter = fnullop_kqfilter, 105 1.31 dsl .fo_restart = fnullop_restart, 106 1.17 jmcneill }; 107 1.17 jmcneill 108 1.1 drochner #define MAXLOCATORS 100 109 1.1 drochner 110 1.40 pgoyette static int (*saved_insert_vec)(const char *, prop_dictionary_t) = NULL; 111 1.40 pgoyette 112 1.17 jmcneill static int drvctl_command(struct lwp *, struct plistref *, u_long, int); 113 1.17 jmcneill static int drvctl_getevent(struct lwp *, struct plistref *, u_long, int); 114 1.17 jmcneill 115 1.17 jmcneill void 116 1.17 jmcneill drvctl_init(void) 117 1.17 jmcneill { 118 1.17 jmcneill TAILQ_INIT(&drvctl_eventq); 119 1.17 jmcneill mutex_init(&drvctl_lock, MUTEX_DEFAULT, IPL_NONE); 120 1.17 jmcneill cv_init(&drvctl_cond, "devmon"); 121 1.20 jmcneill selinit(&drvctl_rdsel); 122 1.17 jmcneill } 123 1.17 jmcneill 124 1.17 jmcneill void 125 1.40 pgoyette drvctl_fini(void) 126 1.40 pgoyette { 127 1.40 pgoyette 128 1.40 pgoyette seldestroy(&drvctl_rdsel); 129 1.40 pgoyette cv_destroy(&drvctl_cond); 130 1.40 pgoyette mutex_destroy(&drvctl_lock); 131 1.40 pgoyette } 132 1.40 pgoyette 133 1.40 pgoyette int 134 1.17 jmcneill devmon_insert(const char *event, prop_dictionary_t ev) 135 1.17 jmcneill { 136 1.21 yamt struct drvctl_event *dce, *odce; 137 1.17 jmcneill 138 1.17 jmcneill mutex_enter(&drvctl_lock); 139 1.17 jmcneill 140 1.17 jmcneill if (drvctl_nopen == 0) { 141 1.34 msaitoh prop_object_release(ev); 142 1.17 jmcneill mutex_exit(&drvctl_lock); 143 1.40 pgoyette return 0; 144 1.17 jmcneill } 145 1.17 jmcneill 146 1.17 jmcneill /* Fill in mandatory member */ 147 1.45 thorpej if (!prop_dictionary_set_string_nocopy(ev, "event", event)) { 148 1.17 jmcneill prop_object_release(ev); 149 1.17 jmcneill mutex_exit(&drvctl_lock); 150 1.40 pgoyette return 0; 151 1.17 jmcneill } 152 1.17 jmcneill 153 1.17 jmcneill dce = kmem_alloc(sizeof(*dce), KM_SLEEP); 154 1.17 jmcneill dce->dce_event = ev; 155 1.17 jmcneill 156 1.17 jmcneill if (drvctl_eventcnt == DRVCTL_EVENTQ_DEPTH) { 157 1.17 jmcneill odce = TAILQ_FIRST(&drvctl_eventq); 158 1.17 jmcneill TAILQ_REMOVE(&drvctl_eventq, odce, dce_link); 159 1.18 freza prop_object_release(odce->dce_event); 160 1.18 freza kmem_free(odce, sizeof(*odce)); 161 1.17 jmcneill --drvctl_eventcnt; 162 1.17 jmcneill } 163 1.17 jmcneill 164 1.17 jmcneill TAILQ_INSERT_TAIL(&drvctl_eventq, dce, dce_link); 165 1.17 jmcneill ++drvctl_eventcnt; 166 1.17 jmcneill cv_broadcast(&drvctl_cond); 167 1.20 jmcneill selnotify(&drvctl_rdsel, 0, 0); 168 1.17 jmcneill 169 1.17 jmcneill mutex_exit(&drvctl_lock); 170 1.40 pgoyette return 0; 171 1.17 jmcneill } 172 1.17 jmcneill 173 1.17 jmcneill int 174 1.17 jmcneill drvctlopen(dev_t dev, int flags, int mode, struct lwp *l) 175 1.17 jmcneill { 176 1.17 jmcneill struct file *fp; 177 1.17 jmcneill int fd; 178 1.17 jmcneill int ret; 179 1.17 jmcneill 180 1.17 jmcneill ret = fd_allocfile(&fp, &fd); 181 1.17 jmcneill if (ret) 182 1.28 dyoung return ret; 183 1.17 jmcneill 184 1.17 jmcneill /* XXX setup context */ 185 1.17 jmcneill mutex_enter(&drvctl_lock); 186 1.17 jmcneill ret = fd_clone(fp, fd, flags, &drvctl_fileops, /* context */NULL); 187 1.17 jmcneill ++drvctl_nopen; 188 1.17 jmcneill mutex_exit(&drvctl_lock); 189 1.17 jmcneill 190 1.17 jmcneill return ret; 191 1.17 jmcneill } 192 1.5 thorpej 193 1.12 dyoung static int 194 1.19 gmcgarry pmdevbyname(u_long cmd, struct devpmargs *a) 195 1.12 dyoung { 196 1.33 chs device_t d; 197 1.12 dyoung 198 1.47 riastrad KASSERT(KERNEL_LOCKED_P()); 199 1.47 riastrad 200 1.14 joerg if ((d = device_find_by_xname(a->devname)) == NULL) 201 1.12 dyoung return ENXIO; 202 1.12 dyoung 203 1.12 dyoung switch (cmd) { 204 1.12 dyoung case DRVSUSPENDDEV: 205 1.29 dyoung return pmf_device_recursive_suspend(d, PMF_Q_DRVCTL) ? 0 : EBUSY; 206 1.12 dyoung case DRVRESUMEDEV: 207 1.16 dyoung if (a->flags & DEVPM_F_SUBTREE) { 208 1.29 dyoung return pmf_device_subtree_resume(d, PMF_Q_DRVCTL) 209 1.16 dyoung ? 0 : EBUSY; 210 1.16 dyoung } else { 211 1.29 dyoung return pmf_device_recursive_resume(d, PMF_Q_DRVCTL) 212 1.16 dyoung ? 0 : EBUSY; 213 1.16 dyoung } 214 1.12 dyoung default: 215 1.12 dyoung return EPASSTHROUGH; 216 1.12 dyoung } 217 1.12 dyoung } 218 1.12 dyoung 219 1.12 dyoung static int 220 1.12 dyoung listdevbyname(struct devlistargs *l) 221 1.12 dyoung { 222 1.13 drochner device_t d, child; 223 1.15 dyoung deviter_t di; 224 1.15 dyoung int cnt = 0, idx, error = 0; 225 1.12 dyoung 226 1.47 riastrad KASSERT(KERNEL_LOCKED_P()); 227 1.47 riastrad 228 1.24 joerg if (*l->l_devname == '\0') 229 1.32 plunky d = NULL; 230 1.24 joerg else if (memchr(l->l_devname, 0, sizeof(l->l_devname)) == NULL) 231 1.24 joerg return EINVAL; 232 1.24 joerg else if ((d = device_find_by_xname(l->l_devname)) == NULL) 233 1.12 dyoung return ENXIO; 234 1.12 dyoung 235 1.15 dyoung for (child = deviter_first(&di, 0); child != NULL; 236 1.15 dyoung child = deviter_next(&di)) { 237 1.13 drochner if (device_parent(child) != d) 238 1.13 drochner continue; 239 1.13 drochner idx = cnt++; 240 1.13 drochner if (l->l_childname == NULL || idx >= l->l_children) 241 1.13 drochner continue; 242 1.13 drochner error = copyoutstr(device_xname(child), l->l_childname[idx], 243 1.13 drochner sizeof(l->l_childname[idx]), NULL); 244 1.15 dyoung if (error != 0) 245 1.15 dyoung break; 246 1.13 drochner } 247 1.15 dyoung deviter_release(&di); 248 1.12 dyoung 249 1.13 drochner l->l_children = cnt; 250 1.15 dyoung return error; 251 1.12 dyoung } 252 1.12 dyoung 253 1.1 drochner static int 254 1.1 drochner detachdevbyname(const char *devname) 255 1.1 drochner { 256 1.33 chs device_t d; 257 1.48 riastrad deviter_t di; 258 1.48 riastrad int error; 259 1.1 drochner 260 1.47 riastrad KASSERT(KERNEL_LOCKED_P()); 261 1.47 riastrad 262 1.48 riastrad for (d = deviter_first(&di, DEVITER_F_RW); 263 1.48 riastrad d != NULL; 264 1.48 riastrad d = deviter_next(&di)) { 265 1.48 riastrad if (strcmp(device_xname(d), devname) == 0) 266 1.48 riastrad break; 267 1.48 riastrad } 268 1.48 riastrad if (d == NULL) { 269 1.48 riastrad error = ENXIO; 270 1.48 riastrad goto out; 271 1.48 riastrad } 272 1.12 dyoung 273 1.1 drochner #ifndef XXXFULLRISK 274 1.12 dyoung /* 275 1.12 dyoung * If the parent cannot be notified, it might keep 276 1.12 dyoung * pointers to the detached device. 277 1.12 dyoung * There might be a private notification mechanism, 278 1.28 dyoung * but better play it safe here. 279 1.12 dyoung */ 280 1.50 riastrad if (device_parent(d) && 281 1.50 riastrad !device_cfattach(device_parent(d))->ca_childdetached) { 282 1.48 riastrad error = ENOTSUP; 283 1.48 riastrad goto out; 284 1.48 riastrad } 285 1.1 drochner #endif 286 1.48 riastrad 287 1.48 riastrad error = config_detach(d, 0); 288 1.48 riastrad out: deviter_release(&di); 289 1.48 riastrad return error; 290 1.1 drochner } 291 1.1 drochner 292 1.1 drochner static int 293 1.1 drochner rescanbus(const char *busname, const char *ifattr, 294 1.1 drochner int numlocators, const int *locators) 295 1.1 drochner { 296 1.12 dyoung int i, rc; 297 1.33 chs device_t d; 298 1.2 drochner const struct cfiattrdata * const *ap; 299 1.1 drochner 300 1.47 riastrad KASSERT(KERNEL_LOCKED_P()); 301 1.47 riastrad 302 1.1 drochner /* XXX there should be a way to get limits and defaults (per device) 303 1.1 drochner from config generated data */ 304 1.1 drochner int locs[MAXLOCATORS]; 305 1.1 drochner for (i = 0; i < MAXLOCATORS; i++) 306 1.1 drochner locs[i] = -1; 307 1.1 drochner 308 1.1 drochner for (i = 0; i < numlocators;i++) 309 1.1 drochner locs[i] = locators[i]; 310 1.1 drochner 311 1.14 joerg if ((d = device_find_by_xname(busname)) == NULL) 312 1.12 dyoung return ENXIO; 313 1.1 drochner 314 1.12 dyoung /* 315 1.12 dyoung * must support rescan, and must have something 316 1.12 dyoung * to attach to 317 1.12 dyoung */ 318 1.50 riastrad if (!device_cfattach(d)->ca_rescan || 319 1.50 riastrad !device_cfdriver(d)->cd_attrs) 320 1.28 dyoung return ENODEV; 321 1.12 dyoung 322 1.49 riastrad /* rescan all ifattrs if none is specified */ 323 1.12 dyoung if (!ifattr) { 324 1.49 riastrad rc = 0; 325 1.50 riastrad for (ap = device_cfdriver(d)->cd_attrs; *ap; ap++) { 326 1.50 riastrad rc = (*device_cfattach(d)->ca_rescan)(d, 327 1.50 riastrad (*ap)->ci_name, locs); 328 1.49 riastrad if (rc) 329 1.49 riastrad break; 330 1.49 riastrad } 331 1.12 dyoung } else { 332 1.12 dyoung /* check for valid attribute passed */ 333 1.50 riastrad for (ap = device_cfdriver(d)->cd_attrs; *ap; ap++) 334 1.12 dyoung if (!strcmp((*ap)->ci_name, ifattr)) 335 1.12 dyoung break; 336 1.12 dyoung if (!*ap) 337 1.28 dyoung return EINVAL; 338 1.50 riastrad rc = (*device_cfattach(d)->ca_rescan)(d, ifattr, locs); 339 1.1 drochner } 340 1.1 drochner 341 1.12 dyoung config_deferred(NULL); 342 1.12 dyoung return rc; 343 1.1 drochner } 344 1.1 drochner 345 1.17 jmcneill static int 346 1.17 jmcneill drvctl_read(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred, 347 1.17 jmcneill int flags) 348 1.17 jmcneill { 349 1.28 dyoung return ENODEV; 350 1.17 jmcneill } 351 1.17 jmcneill 352 1.17 jmcneill static int 353 1.17 jmcneill drvctl_write(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred, 354 1.17 jmcneill int flags) 355 1.17 jmcneill { 356 1.28 dyoung return ENODEV; 357 1.17 jmcneill } 358 1.17 jmcneill 359 1.17 jmcneill static int 360 1.17 jmcneill drvctl_ioctl(struct file *fp, u_long cmd, void *data) 361 1.1 drochner { 362 1.1 drochner int res; 363 1.1 drochner char *ifattr; 364 1.1 drochner int *locs; 365 1.22 yamt size_t locs_sz = 0; /* XXXgcc */ 366 1.1 drochner 367 1.46 riastrad KERNEL_LOCK(1, NULL); 368 1.1 drochner switch (cmd) { 369 1.12 dyoung case DRVSUSPENDDEV: 370 1.12 dyoung case DRVRESUMEDEV: 371 1.12 dyoung #define d ((struct devpmargs *)data) 372 1.12 dyoung res = pmdevbyname(cmd, d); 373 1.12 dyoung #undef d 374 1.12 dyoung break; 375 1.12 dyoung case DRVLISTDEV: 376 1.12 dyoung res = listdevbyname((struct devlistargs *)data); 377 1.12 dyoung break; 378 1.1 drochner case DRVDETACHDEV: 379 1.1 drochner #define d ((struct devdetachargs *)data) 380 1.1 drochner res = detachdevbyname(d->devname); 381 1.1 drochner #undef d 382 1.1 drochner break; 383 1.1 drochner case DRVRESCANBUS: 384 1.1 drochner #define d ((struct devrescanargs *)data) 385 1.1 drochner d->busname[sizeof(d->busname) - 1] = '\0'; 386 1.1 drochner 387 1.1 drochner /* XXX better copyin? */ 388 1.1 drochner if (d->ifattr[0]) { 389 1.1 drochner d->ifattr[sizeof(d->ifattr) - 1] = '\0'; 390 1.1 drochner ifattr = d->ifattr; 391 1.1 drochner } else 392 1.1 drochner ifattr = 0; 393 1.1 drochner 394 1.1 drochner if (d->numlocators) { 395 1.46 riastrad if (d->numlocators > MAXLOCATORS) { 396 1.46 riastrad res = EINVAL; 397 1.46 riastrad goto out; 398 1.46 riastrad } 399 1.22 yamt locs_sz = d->numlocators * sizeof(int); 400 1.22 yamt locs = kmem_alloc(locs_sz, KM_SLEEP); 401 1.22 yamt res = copyin(d->locators, locs, locs_sz); 402 1.11 rmind if (res) { 403 1.22 yamt kmem_free(locs, locs_sz); 404 1.46 riastrad goto out; 405 1.11 rmind } 406 1.1 drochner } else 407 1.22 yamt locs = NULL; 408 1.1 drochner res = rescanbus(d->busname, ifattr, d->numlocators, locs); 409 1.1 drochner if (locs) 410 1.22 yamt kmem_free(locs, locs_sz); 411 1.1 drochner #undef d 412 1.5 thorpej break; 413 1.5 thorpej case DRVCTLCOMMAND: 414 1.17 jmcneill res = drvctl_command(curlwp, (struct plistref *)data, cmd, 415 1.17 jmcneill fp->f_flag); 416 1.5 thorpej break; 417 1.17 jmcneill case DRVGETEVENT: 418 1.17 jmcneill res = drvctl_getevent(curlwp, (struct plistref *)data, cmd, 419 1.17 jmcneill fp->f_flag); 420 1.17 jmcneill break; 421 1.5 thorpej default: 422 1.46 riastrad res = EPASSTHROUGH; 423 1.46 riastrad break; 424 1.1 drochner } 425 1.46 riastrad out: KERNEL_UNLOCK_ONE(NULL); 426 1.28 dyoung return res; 427 1.1 drochner } 428 1.1 drochner 429 1.17 jmcneill static int 430 1.25 christos drvctl_stat(struct file *fp, struct stat *st) 431 1.25 christos { 432 1.25 christos (void)memset(st, 0, sizeof(*st)); 433 1.26 christos st->st_uid = kauth_cred_geteuid(fp->f_cred); 434 1.26 christos st->st_gid = kauth_cred_getegid(fp->f_cred); 435 1.25 christos return 0; 436 1.25 christos } 437 1.25 christos 438 1.25 christos static int 439 1.20 jmcneill drvctl_poll(struct file *fp, int events) 440 1.20 jmcneill { 441 1.20 jmcneill int revents = 0; 442 1.20 jmcneill 443 1.20 jmcneill if (!TAILQ_EMPTY(&drvctl_eventq)) 444 1.20 jmcneill revents |= events & (POLLIN | POLLRDNORM); 445 1.20 jmcneill else 446 1.20 jmcneill selrecord(curlwp, &drvctl_rdsel); 447 1.20 jmcneill 448 1.20 jmcneill return revents; 449 1.20 jmcneill } 450 1.20 jmcneill 451 1.20 jmcneill static int 452 1.17 jmcneill drvctl_close(struct file *fp) 453 1.17 jmcneill { 454 1.17 jmcneill struct drvctl_event *dce; 455 1.17 jmcneill 456 1.17 jmcneill /* XXX free context */ 457 1.17 jmcneill mutex_enter(&drvctl_lock); 458 1.17 jmcneill KASSERT(drvctl_nopen > 0); 459 1.17 jmcneill --drvctl_nopen; 460 1.17 jmcneill if (drvctl_nopen == 0) { 461 1.17 jmcneill /* flush queue */ 462 1.17 jmcneill while ((dce = TAILQ_FIRST(&drvctl_eventq)) != NULL) { 463 1.17 jmcneill TAILQ_REMOVE(&drvctl_eventq, dce, dce_link); 464 1.17 jmcneill KASSERT(drvctl_eventcnt > 0); 465 1.17 jmcneill --drvctl_eventcnt; 466 1.18 freza prop_object_release(dce->dce_event); 467 1.17 jmcneill kmem_free(dce, sizeof(*dce)); 468 1.17 jmcneill } 469 1.17 jmcneill } 470 1.17 jmcneill mutex_exit(&drvctl_lock); 471 1.17 jmcneill 472 1.28 dyoung return 0; 473 1.17 jmcneill } 474 1.17 jmcneill 475 1.1 drochner void 476 1.38 uebayasi drvctlattach(int arg __unused) 477 1.1 drochner { 478 1.1 drochner } 479 1.5 thorpej 480 1.5 thorpej /***************************************************************************** 481 1.5 thorpej * Driver control command processing engine 482 1.5 thorpej *****************************************************************************/ 483 1.5 thorpej 484 1.5 thorpej static int 485 1.9 yamt drvctl_command_get_properties(struct lwp *l, 486 1.5 thorpej prop_dictionary_t command_dict, 487 1.5 thorpej prop_dictionary_t results_dict) 488 1.5 thorpej { 489 1.5 thorpej prop_dictionary_t args_dict; 490 1.5 thorpej prop_string_t devname_string; 491 1.5 thorpej device_t dev; 492 1.15 dyoung deviter_t di; 493 1.5 thorpej 494 1.5 thorpej args_dict = prop_dictionary_get(command_dict, "drvctl-arguments"); 495 1.5 thorpej if (args_dict == NULL) 496 1.28 dyoung return EINVAL; 497 1.5 thorpej 498 1.5 thorpej devname_string = prop_dictionary_get(args_dict, "device-name"); 499 1.5 thorpej if (devname_string == NULL) 500 1.28 dyoung return EINVAL; 501 1.5 thorpej 502 1.15 dyoung for (dev = deviter_first(&di, 0); dev != NULL; 503 1.15 dyoung dev = deviter_next(&di)) { 504 1.45 thorpej if (prop_string_equals_string(devname_string, 505 1.15 dyoung device_xname(dev))) { 506 1.15 dyoung prop_dictionary_set(results_dict, "drvctl-result-data", 507 1.15 dyoung device_properties(dev)); 508 1.5 thorpej break; 509 1.15 dyoung } 510 1.5 thorpej } 511 1.5 thorpej 512 1.15 dyoung deviter_release(&di); 513 1.15 dyoung 514 1.5 thorpej if (dev == NULL) 515 1.28 dyoung return ESRCH; 516 1.15 dyoung 517 1.28 dyoung return 0; 518 1.5 thorpej } 519 1.5 thorpej 520 1.5 thorpej struct drvctl_command_desc { 521 1.5 thorpej const char *dcd_name; /* command name */ 522 1.5 thorpej int (*dcd_func)(struct lwp *, /* handler function */ 523 1.5 thorpej prop_dictionary_t, 524 1.5 thorpej prop_dictionary_t); 525 1.5 thorpej int dcd_rw; /* read or write required */ 526 1.5 thorpej }; 527 1.5 thorpej 528 1.5 thorpej static const struct drvctl_command_desc drvctl_command_table[] = { 529 1.5 thorpej { .dcd_name = "get-properties", 530 1.5 thorpej .dcd_func = drvctl_command_get_properties, 531 1.5 thorpej .dcd_rw = FREAD, 532 1.5 thorpej }, 533 1.5 thorpej 534 1.5 thorpej { .dcd_name = NULL } 535 1.5 thorpej }; 536 1.5 thorpej 537 1.5 thorpej static int 538 1.5 thorpej drvctl_command(struct lwp *l, struct plistref *pref, u_long ioctl_cmd, 539 1.5 thorpej int fflag) 540 1.5 thorpej { 541 1.5 thorpej prop_dictionary_t command_dict, results_dict; 542 1.5 thorpej prop_string_t command_string; 543 1.5 thorpej const struct drvctl_command_desc *dcd; 544 1.5 thorpej int error; 545 1.5 thorpej 546 1.5 thorpej error = prop_dictionary_copyin_ioctl(pref, ioctl_cmd, &command_dict); 547 1.5 thorpej if (error) 548 1.28 dyoung return error; 549 1.5 thorpej 550 1.5 thorpej results_dict = prop_dictionary_create(); 551 1.5 thorpej if (results_dict == NULL) { 552 1.5 thorpej prop_object_release(command_dict); 553 1.28 dyoung return ENOMEM; 554 1.5 thorpej } 555 1.5 thorpej 556 1.5 thorpej command_string = prop_dictionary_get(command_dict, "drvctl-command"); 557 1.5 thorpej if (command_string == NULL) { 558 1.5 thorpej error = EINVAL; 559 1.5 thorpej goto out; 560 1.5 thorpej } 561 1.5 thorpej 562 1.5 thorpej for (dcd = drvctl_command_table; dcd->dcd_name != NULL; dcd++) { 563 1.45 thorpej if (prop_string_equals_string(command_string, 564 1.45 thorpej dcd->dcd_name)) 565 1.5 thorpej break; 566 1.5 thorpej } 567 1.5 thorpej 568 1.5 thorpej if (dcd->dcd_name == NULL) { 569 1.5 thorpej error = EINVAL; 570 1.5 thorpej goto out; 571 1.5 thorpej } 572 1.5 thorpej 573 1.5 thorpej if ((fflag & dcd->dcd_rw) == 0) { 574 1.5 thorpej error = EPERM; 575 1.5 thorpej goto out; 576 1.5 thorpej } 577 1.5 thorpej 578 1.5 thorpej error = (*dcd->dcd_func)(l, command_dict, results_dict); 579 1.5 thorpej 580 1.8 thorpej prop_dictionary_set_int32(results_dict, "drvctl-error", error); 581 1.5 thorpej 582 1.5 thorpej error = prop_dictionary_copyout_ioctl(pref, ioctl_cmd, results_dict); 583 1.5 thorpej out: 584 1.5 thorpej prop_object_release(command_dict); 585 1.5 thorpej prop_object_release(results_dict); 586 1.28 dyoung return error; 587 1.5 thorpej } 588 1.17 jmcneill 589 1.17 jmcneill static int 590 1.17 jmcneill drvctl_getevent(struct lwp *l, struct plistref *pref, u_long ioctl_cmd, 591 1.17 jmcneill int fflag) 592 1.17 jmcneill { 593 1.17 jmcneill struct drvctl_event *dce; 594 1.17 jmcneill int ret; 595 1.17 jmcneill 596 1.17 jmcneill if ((fflag & (FREAD|FWRITE)) != (FREAD|FWRITE)) 597 1.28 dyoung return EPERM; 598 1.17 jmcneill 599 1.17 jmcneill mutex_enter(&drvctl_lock); 600 1.17 jmcneill while ((dce = TAILQ_FIRST(&drvctl_eventq)) == NULL) { 601 1.17 jmcneill if (fflag & O_NONBLOCK) { 602 1.17 jmcneill mutex_exit(&drvctl_lock); 603 1.28 dyoung return EWOULDBLOCK; 604 1.17 jmcneill } 605 1.17 jmcneill 606 1.17 jmcneill ret = cv_wait_sig(&drvctl_cond, &drvctl_lock); 607 1.17 jmcneill if (ret) { 608 1.17 jmcneill mutex_exit(&drvctl_lock); 609 1.28 dyoung return ret; 610 1.17 jmcneill } 611 1.17 jmcneill } 612 1.17 jmcneill TAILQ_REMOVE(&drvctl_eventq, dce, dce_link); 613 1.17 jmcneill KASSERT(drvctl_eventcnt > 0); 614 1.17 jmcneill --drvctl_eventcnt; 615 1.17 jmcneill mutex_exit(&drvctl_lock); 616 1.17 jmcneill 617 1.17 jmcneill ret = prop_dictionary_copyout_ioctl(pref, ioctl_cmd, dce->dce_event); 618 1.17 jmcneill 619 1.17 jmcneill prop_object_release(dce->dce_event); 620 1.17 jmcneill kmem_free(dce, sizeof(*dce)); 621 1.17 jmcneill 622 1.28 dyoung return ret; 623 1.17 jmcneill } 624 1.40 pgoyette 625 1.40 pgoyette /* 626 1.40 pgoyette * Module glue 627 1.40 pgoyette */ 628 1.40 pgoyette 629 1.40 pgoyette MODULE(MODULE_CLASS_DRIVER, drvctl, NULL); 630 1.40 pgoyette 631 1.40 pgoyette int 632 1.40 pgoyette drvctl_modcmd(modcmd_t cmd, void *arg) 633 1.40 pgoyette { 634 1.40 pgoyette int error; 635 1.40 pgoyette #ifdef _MODULE 636 1.40 pgoyette int bmajor, cmajor; 637 1.40 pgoyette #endif 638 1.40 pgoyette 639 1.40 pgoyette error = 0; 640 1.40 pgoyette switch (cmd) { 641 1.40 pgoyette case MODULE_CMD_INIT: 642 1.40 pgoyette drvctl_init(); 643 1.40 pgoyette 644 1.41 christos mutex_enter(&drvctl_lock); 645 1.40 pgoyette #ifdef _MODULE 646 1.40 pgoyette bmajor = cmajor = -1; 647 1.40 pgoyette error = devsw_attach("drvctl", NULL, &bmajor, 648 1.40 pgoyette &drvctl_cdevsw, &cmajor); 649 1.40 pgoyette #endif 650 1.40 pgoyette if (error == 0) { 651 1.40 pgoyette KASSERT(saved_insert_vec == NULL); 652 1.40 pgoyette saved_insert_vec = devmon_insert_vec; 653 1.40 pgoyette devmon_insert_vec = devmon_insert; 654 1.40 pgoyette } 655 1.40 pgoyette 656 1.40 pgoyette mutex_exit(&drvctl_lock); 657 1.40 pgoyette break; 658 1.40 pgoyette 659 1.40 pgoyette case MODULE_CMD_FINI: 660 1.40 pgoyette mutex_enter(&drvctl_lock); 661 1.40 pgoyette if (drvctl_nopen != 0 || drvctl_eventcnt != 0 ) { 662 1.40 pgoyette mutex_exit(&drvctl_lock); 663 1.40 pgoyette return EBUSY; 664 1.40 pgoyette } 665 1.40 pgoyette KASSERT(saved_insert_vec != NULL); 666 1.40 pgoyette devmon_insert_vec = saved_insert_vec; 667 1.40 pgoyette saved_insert_vec = NULL; 668 1.40 pgoyette #ifdef _MODULE 669 1.51 riastrad devsw_detach(NULL, &drvctl_cdevsw); 670 1.40 pgoyette #endif 671 1.40 pgoyette mutex_exit(&drvctl_lock); 672 1.51 riastrad drvctl_fini(); 673 1.40 pgoyette 674 1.40 pgoyette break; 675 1.40 pgoyette default: 676 1.40 pgoyette error = ENOTTY; 677 1.40 pgoyette break; 678 1.40 pgoyette } 679 1.40 pgoyette 680 1.40 pgoyette return error; 681 1.40 pgoyette } 682