| Commit | Line | Data |
|---|---|---|
| 32e913d7 JT |
1 | /*- |
| 2 | * Copyright (c) 2005-2008, Sam Leffler <sam@errno.com> | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice unmodified, this list of conditions, and the following | |
| 10 | * disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| 14 | * | |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 89e93e6f RP |
25 | * |
| 26 | * $FreeBSD: src/sys/kern/subr_firmware.c,v 1.13.2.2 2010/02/11 18:34:06 mjacob Exp $ | |
| 32e913d7 JT |
27 | */ |
| 28 | ||
| 32e913d7 JT |
29 | #include <sys/param.h> |
| 30 | #include <sys/kernel.h> | |
| 31 | #include <sys/malloc.h> | |
| 32 | #include <sys/queue.h> | |
| 33 | #include <sys/taskqueue.h> | |
| 34 | #include <sys/systm.h> | |
| 35 | #include <sys/lock.h> | |
| 36 | #include <sys/spinlock.h> | |
| 37 | #include <sys/spinlock2.h> | |
| 38 | #include <sys/errno.h> | |
| 39 | #include <sys/linker.h> | |
| d83c779a | 40 | #include <sys/firmware.h> |
| 32e913d7 JT |
41 | #include <sys/priv.h> |
| 42 | #include <sys/proc.h> | |
| 43 | #include <sys/module.h> | |
| 44 | #include <sys/eventhandler.h> | |
| 45 | ||
| 46 | #include <sys/filedesc.h> | |
| 47 | #include <sys/vnode.h> | |
| 48 | ||
| 49 | /* | |
| 50 | * Loadable firmware support. See sys/sys/firmware.h and firmware(9) | |
| 51 | * form more details on the subsystem. | |
| 52 | * | |
| 53 | * 'struct firmware' is the user-visible part of the firmware table. | |
| 54 | * Additional internal information is stored in a 'struct priv_fw' | |
| 55 | * (currently a static array). A slot is in use if FW_INUSE is true: | |
| 56 | */ | |
| 57 | ||
| 58 | #define FW_INUSE(p) ((p)->file != NULL || (p)->fw.name != NULL) | |
| 59 | ||
| 60 | /* | |
| 61 | * fw.name != NULL when an image is registered; file != NULL for | |
| 62 | * autoloaded images whose handling has not been completed. | |
| 63 | * | |
| 64 | * The state of a slot evolves as follows: | |
| 65 | * firmware_register --> fw.name = image_name | |
| 66 | * (autoloaded image) --> file = module reference | |
| 67 | * firmware_unregister --> fw.name = NULL | |
| 68 | * (unloadentry complete) --> file = NULL | |
| 69 | * | |
| 70 | * In order for the above to work, the 'file' field must remain | |
| 71 | * unchanged in firmware_unregister(). | |
| 72 | * | |
| 73 | * Images residing in the same module are linked to each other | |
| 74 | * through the 'parent' argument of firmware_register(). | |
| 75 | * One image (typically, one with the same name as the module to let | |
| 76 | * the autoloading mechanism work) is considered the parent image for | |
| 77 | * all other images in the same module. Children affect the refcount | |
| 78 | * on the parent image preventing improper unloading of the image itself. | |
| 79 | */ | |
| 80 | ||
| 81 | struct priv_fw { | |
| 82 | int refcnt; /* reference count */ | |
| 83 | ||
| 84 | /* | |
| 85 | * parent entry, see above. Set on firmware_register(), | |
| 86 | * cleared on firmware_unregister(). | |
| 87 | */ | |
| 88 | struct priv_fw *parent; | |
| 89 | ||
| 90 | int flags; /* record FIRMWARE_UNLOAD requests */ | |
| 91 | #define FW_UNLOAD 0x100 | |
| 92 | ||
| 93 | /* | |
| 94 | * 'file' is private info managed by the autoload/unload code. | |
| 95 | * Set at the end of firmware_get(), cleared only in the | |
| 96 | * firmware_unload_task, so the latter can depend on its value even | |
| 97 | * while the lock is not held. | |
| 98 | */ | |
| 99 | linker_file_t file; /* module file, if autoloaded */ | |
| 100 | ||
| 101 | /* | |
| 102 | * 'fw' is the externally visible image information. | |
| 103 | * We do not make it the first field in priv_fw, to avoid the | |
| 104 | * temptation of casting pointers to each other. | |
| 105 | * Use PRIV_FW(fw) to get a pointer to the cointainer of fw. | |
| 106 | * Beware, PRIV_FW does not work for a NULL pointer. | |
| 107 | */ | |
| 108 | struct firmware fw; /* externally visible information */ | |
| 109 | }; | |
| 110 | ||
| 111 | /* | |
| 112 | * PRIV_FW returns the pointer to the container of struct firmware *x. | |
| 113 | * Cast to intptr_t to override the 'const' attribute of x | |
| 114 | */ | |
| 115 | #define PRIV_FW(x) ((struct priv_fw *) \ | |
| 116 | ((intptr_t)(x) - offsetof(struct priv_fw, fw)) ) | |
| 117 | ||
| 118 | /* | |
| 119 | * At the moment we use a static array as backing store for the registry. | |
| 120 | * Should we move to a dynamic structure, keep in mind that we cannot | |
| 121 | * reallocate the array because pointers are held externally. | |
| 122 | * A list may work, though. | |
| 123 | */ | |
| 124 | #define FIRMWARE_MAX 30 | |
| 125 | static struct priv_fw firmware_table[FIRMWARE_MAX]; | |
| 126 | ||
| 127 | /* | |
| 128 | * Firmware module operations are handled in a separate task as they | |
| 129 | * might sleep and they require directory context to do i/o. | |
| 130 | */ | |
| 131 | static struct taskqueue *firmware_tq; | |
| 132 | static struct task firmware_unload_task; | |
| 133 | ||
| 134 | /* | |
| 135 | * This mutex protects accesses to the firmware table. | |
| 136 | */ | |
| 137 | static struct lock firmware_lock; | |
| 138 | #if 0 | |
| 139 | MTX_SYSINIT(firmware, &firmware_lock, "firmware table", MTX_DEF); | |
| 140 | #endif | |
| 141 | ||
| 142 | /* | |
| 143 | * Helper function to lookup a name. | |
| 144 | * As a side effect, it sets the pointer to a free slot, if any. | |
| 145 | * This way we can concentrate most of the registry scanning in | |
| 146 | * this function, which makes it easier to replace the registry | |
| 147 | * with some other data structure. | |
| 148 | */ | |
| 149 | static struct priv_fw * | |
| 150 | lookup(const char *name, struct priv_fw **empty_slot) | |
| 151 | { | |
| 152 | struct priv_fw *fp = NULL; | |
| 153 | struct priv_fw *dummy; | |
| 154 | int i; | |
| 155 | ||
| 156 | if (empty_slot == NULL) | |
| 157 | empty_slot = &dummy; | |
| 158 | *empty_slot = NULL; | |
| 159 | for (i = 0; i < FIRMWARE_MAX; i++) { | |
| 160 | fp = &firmware_table[i]; | |
| 161 | if (fp->fw.name != NULL && strcasecmp(name, fp->fw.name) == 0) | |
| 162 | break; | |
| 163 | else if (!FW_INUSE(fp)) | |
| 164 | *empty_slot = fp; | |
| 165 | } | |
| 166 | return (i < FIRMWARE_MAX ) ? fp : NULL; | |
| 167 | } | |
| 168 | ||
| 169 | /* | |
| 170 | * Register a firmware image with the specified name. The | |
| 171 | * image name must not already be registered. If this is a | |
| 172 | * subimage then parent refers to a previously registered | |
| 173 | * image that this should be associated with. | |
| 174 | */ | |
| 175 | const struct firmware * | |
| 176 | firmware_register(const char *imagename, const void *data, size_t datasize, | |
| 177 | unsigned int version, const struct firmware *parent) | |
| 178 | { | |
| 179 | struct priv_fw *match, *frp; | |
| 180 | ||
| 181 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 182 | /* | |
| 183 | * Do a lookup to make sure the name is unique or find a free slot. | |
| 184 | */ | |
| 185 | match = lookup(imagename, &frp); | |
| 186 | if (match != NULL) { | |
| 187 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 188 | kprintf("%s: image %s already registered!\n", | |
| 189 | __func__, imagename); | |
| 190 | return NULL; | |
| 191 | } | |
| 192 | if (frp == NULL) { | |
| 193 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 194 | kprintf("%s: cannot register image %s, firmware table full!\n", | |
| 195 | __func__, imagename); | |
| 196 | return NULL; | |
| 197 | } | |
| 8075c3b8 | 198 | bzero(frp, sizeof(*frp)); /* start from a clean record */ |
| 32e913d7 JT |
199 | frp->fw.name = imagename; |
| 200 | frp->fw.data = data; | |
| 201 | frp->fw.datasize = datasize; | |
| 202 | frp->fw.version = version; | |
| 203 | if (parent != NULL) { | |
| 204 | frp->parent = PRIV_FW(parent); | |
| 205 | frp->parent->refcnt++; | |
| 206 | } | |
| 207 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 208 | if (bootverbose) | |
| 209 | kprintf("firmware: '%s' version %u: %zu bytes loaded at %p\n", | |
| 210 | imagename, version, datasize, data); | |
| 211 | return &frp->fw; | |
| 212 | } | |
| 213 | ||
| 214 | /* | |
| 215 | * Unregister/remove a firmware image. If there are outstanding | |
| 216 | * references an error is returned and the image is not removed | |
| 217 | * from the registry. | |
| 218 | */ | |
| 219 | int | |
| 220 | firmware_unregister(const char *imagename) | |
| 221 | { | |
| 222 | struct priv_fw *fp; | |
| 223 | int err; | |
| 224 | ||
| 225 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 226 | fp = lookup(imagename, NULL); | |
| 227 | if (fp == NULL) { | |
| 228 | /* | |
| 229 | * It is ok for the lookup to fail; this can happen | |
| 230 | * when a module is unloaded on last reference and the | |
| 231 | * module unload handler unregister's each of it's | |
| 232 | * firmware images. | |
| 233 | */ | |
| 234 | err = 0; | |
| 235 | } else if (fp->refcnt != 0) { /* cannot unregister */ | |
| 236 | err = EBUSY; | |
| 237 | } else { | |
| 238 | linker_file_t x = fp->file; /* save value */ | |
| 239 | ||
| 240 | if (fp->parent != NULL) /* release parent reference */ | |
| 241 | fp->parent->refcnt--; | |
| 242 | /* | |
| 243 | * Clear the whole entry with bzero to make sure we | |
| 244 | * do not forget anything. Then restore 'file' which is | |
| 245 | * non-null for autoloaded images. | |
| 246 | */ | |
| 247 | bzero(fp, sizeof(struct priv_fw)); | |
| 248 | fp->file = x; | |
| 249 | err = 0; | |
| 250 | } | |
| 251 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 252 | return err; | |
| 253 | } | |
| 254 | ||
| 255 | static void | |
| 256 | loadimage(void *arg, int npending) | |
| 257 | { | |
| 89e93e6f | 258 | #ifdef notyet |
| 32e913d7 | 259 | struct thread *td = curthread; |
| 89e93e6f | 260 | #endif |
| 32e913d7 JT |
261 | char *imagename = arg; |
| 262 | struct priv_fw *fp; | |
| 263 | linker_file_t result; | |
| 264 | int error; | |
| 265 | ||
| 266 | /* synchronize with the thread that dispatched us */ | |
| 267 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 268 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 269 | ||
| 270 | /* JAT | |
| 271 | if (td->td_proc->p_fd->fd_rdir == NULL) { | |
| 272 | kprintf("%s: root not mounted yet, no way to load image\n", | |
| 273 | imagename); | |
| 274 | goto done; | |
| 275 | } | |
| 276 | */ | |
| 277 | error = linker_reference_module(imagename, NULL, &result); | |
| 278 | if (error != 0) { | |
| 279 | kprintf("%s: could not load firmware image, error %d\n", | |
| 280 | imagename, error); | |
| 281 | goto done; | |
| 282 | } | |
| 283 | ||
| 284 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 285 | fp = lookup(imagename, NULL); | |
| 286 | if (fp == NULL || fp->file != NULL) { | |
| 287 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 288 | if (fp == NULL) | |
| 289 | kprintf("%s: firmware image loaded, " | |
| 290 | "but did not register\n", imagename); | |
| 291 | (void) linker_release_module(imagename, NULL, NULL); | |
| 292 | goto done; | |
| 293 | } | |
| 294 | fp->file = result; /* record the module identity */ | |
| 295 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 296 | done: | |
| 297 | wakeup_one(imagename); /* we're done */ | |
| 298 | } | |
| 299 | ||
| 300 | /* | |
| 301 | * Lookup and potentially load the specified firmware image. | |
| 302 | * If the firmware is not found in the registry, try to load a kernel | |
| 303 | * module named as the image name. | |
| 304 | * If the firmware is located, a reference is returned. The caller must | |
| 305 | * release this reference for the image to be eligible for removal/unload. | |
| 306 | */ | |
| 307 | const struct firmware * | |
| 308 | firmware_get(const char *imagename) | |
| 309 | { | |
| 310 | struct task fwload_task; | |
| 311 | struct thread *td; | |
| 312 | struct priv_fw *fp; | |
| 313 | ||
| 314 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 315 | fp = lookup(imagename, NULL); | |
| 316 | if (fp != NULL) | |
| 317 | goto found; | |
| 318 | /* | |
| 319 | * Image not present, try to load the module holding it. | |
| 320 | */ | |
| 321 | td = curthread; | |
| 5c4157c8 | 322 | if (priv_check(td, PRIV_FIRMWARE_LOAD) != 0 || securelevel > 0) { |
| 32e913d7 JT |
323 | lockmgr(&firmware_lock, LK_RELEASE); |
| 324 | kprintf("%s: insufficient privileges to " | |
| 325 | "load firmware image %s\n", __func__, imagename); | |
| 326 | return NULL; | |
| 327 | } | |
| 328 | /* | |
| 329 | * Defer load to a thread with known context. linker_reference_module | |
| 330 | * may do filesystem i/o which requires root & current dirs, etc. | |
| 331 | * Also we must not hold any lock's over this call which is problematic. | |
| 332 | */ | |
| 333 | if (!cold) { | |
| 334 | TASK_INIT(&fwload_task, 0, loadimage, __DECONST(void *, | |
| 335 | imagename)); | |
| 336 | taskqueue_enqueue(firmware_tq, &fwload_task); | |
| 337 | lksleep(__DECONST(void *, imagename), &firmware_lock, 0, | |
| 338 | "fwload", 0); | |
| 339 | } | |
| 340 | /* | |
| 341 | * After attempting to load the module, see if the image is registered. | |
| 342 | */ | |
| 343 | fp = lookup(imagename, NULL); | |
| 344 | if (fp == NULL) { | |
| 345 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 346 | return NULL; | |
| 347 | } | |
| 348 | found: /* common exit point on success */ | |
| 349 | fp->refcnt++; | |
| 350 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 351 | return &fp->fw; | |
| 352 | } | |
| 353 | ||
| 354 | /* | |
| 355 | * Release a reference to a firmware image returned by firmware_get. | |
| 356 | * The caller may specify, with the FIRMWARE_UNLOAD flag, its desire | |
| 357 | * to release the resource, but the flag is only advisory. | |
| 358 | * | |
| 359 | * If this is the last reference to the firmware image, and this is an | |
| 360 | * autoloaded module, wake up the firmware_unload_task to figure out | |
| 361 | * what to do with the associated module. | |
| 362 | */ | |
| 363 | void | |
| 364 | firmware_put(const struct firmware *p, int flags) | |
| 365 | { | |
| 366 | struct priv_fw *fp = PRIV_FW(p); | |
| 367 | ||
| 368 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 369 | fp->refcnt--; | |
| 370 | if (fp->refcnt == 0) { | |
| 371 | if (flags & FIRMWARE_UNLOAD) | |
| 372 | fp->flags |= FW_UNLOAD; | |
| 373 | if (fp->file) | |
| 374 | taskqueue_enqueue(firmware_tq, &firmware_unload_task); | |
| 375 | } | |
| 376 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 377 | } | |
| 378 | ||
| 89e93e6f | 379 | #ifdef notyet |
| 32e913d7 JT |
380 | /* |
| 381 | * Setup directory state for the firmware_tq thread so we can do i/o. | |
| 382 | */ | |
| 383 | static void | |
| 384 | set_rootvnode(void *arg, int npending) | |
| 385 | { | |
| 386 | struct thread *td = curthread; | |
| 387 | struct proc *p = td->td_proc; | |
| 388 | ||
| 389 | ||
| 32e913d7 JT |
390 | #if 0 |
| 391 | spin_lock_wr(&p->p_fd->fd_spin); | |
| 32e913d7 JT |
392 | if (p->p_fd->fd_cdir == NULL) { |
| 393 | p->p_fd->fd_cdir = rootvnode; | |
| 394 | vref(rootvnode); | |
| 395 | } | |
| 396 | if (p->p_fd->fd_rdir == NULL) { | |
| 397 | p->p_fd->fd_rdir = rootvnode; | |
| 398 | vref(rootvnode); | |
| 399 | } | |
| 400 | spin_unlock_wr(&p->p_fd->fd_spin); | |
| 32e913d7 JT |
401 | |
| 402 | kfree(arg, M_TEMP); | |
| 403 | #endif | |
| 404 | } | |
| 405 | ||
| 406 | /* | |
| 407 | * Event handler called on mounting of /; bounce a task | |
| 408 | * into the task queue thread to setup it's directories. | |
| 409 | */ | |
| 410 | static void | |
| 411 | firmware_mountroot(void *arg) | |
| 412 | { | |
| 413 | struct task *setroot_task; | |
| 414 | ||
| 415 | setroot_task = kmalloc(sizeof(struct task), M_TEMP, M_NOWAIT); | |
| 416 | if (setroot_task != NULL) { | |
| 417 | TASK_INIT(setroot_task, 0, set_rootvnode, setroot_task); | |
| 418 | taskqueue_enqueue(firmware_tq, setroot_task); | |
| 419 | } else | |
| 420 | kprintf("%s: no memory for task!\n", __func__); | |
| 421 | } | |
| 32e913d7 JT |
422 | EVENTHANDLER_DECLARE(mountroot, firmware_mountroot); |
| 423 | #endif | |
| 424 | ||
| 425 | /* | |
| 426 | * The body of the task in charge of unloading autoloaded modules | |
| 427 | * that are not needed anymore. | |
| 428 | * Images can be cross-linked so we may need to make multiple passes, | |
| 429 | * but the time we spend in the loop is bounded because we clear entries | |
| 430 | * as we touch them. | |
| 431 | */ | |
| 432 | static void | |
| 433 | unloadentry(void *unused1, int unused2) | |
| 434 | { | |
| 435 | int limit = FIRMWARE_MAX; | |
| 436 | int i; /* current cycle */ | |
| 437 | ||
| 438 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 439 | /* | |
| 440 | * Scan the table. limit is set to make sure we make another | |
| 441 | * full sweep after matching an entry that requires unloading. | |
| 442 | */ | |
| 443 | for (i = 0; i < limit; i++) { | |
| 444 | struct priv_fw *fp; | |
| 445 | int err; | |
| 446 | ||
| 447 | fp = &firmware_table[i % FIRMWARE_MAX]; | |
| 448 | if (fp->fw.name == NULL || fp->file == NULL || | |
| 449 | fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0) | |
| 450 | continue; | |
| 451 | ||
| 452 | /* | |
| 453 | * Found an entry. Now: | |
| 454 | * 1. bump up limit to make sure we make another full round; | |
| 455 | * 2. clear FW_UNLOAD so we don't try this entry again. | |
| 456 | * 3. release the lock while trying to unload the module. | |
| 457 | * 'file' remains set so that the entry cannot be reused | |
| 458 | * in the meantime (it also means that fp->file will | |
| 459 | * not change while we release the lock). | |
| 460 | */ | |
| 461 | limit = i + FIRMWARE_MAX; /* make another full round */ | |
| 462 | fp->flags &= ~FW_UNLOAD; /* do not try again */ | |
| 463 | ||
| 464 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 465 | err = linker_release_module(NULL, NULL, fp->file); | |
| 466 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 467 | ||
| 468 | /* | |
| 469 | * We rely on the module to call firmware_unregister() | |
| 470 | * on unload to actually release the entry. | |
| 471 | * If err = 0 we can drop our reference as the system | |
| 472 | * accepted it. Otherwise unloading failed (e.g. the | |
| 473 | * module itself gave an error) so our reference is | |
| 474 | * still valid. | |
| 475 | */ | |
| 476 | if (err == 0) | |
| 477 | fp->file = NULL; | |
| 478 | } | |
| 479 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 480 | } | |
| 481 | ||
| 482 | /* | |
| 483 | * Module glue. | |
| 484 | */ | |
| 485 | static int | |
| 486 | firmware_modevent(module_t mod, int type, void *unused) | |
| 487 | { | |
| 488 | struct priv_fw *fp; | |
| 489 | int i, err; | |
| 490 | ||
| 491 | switch (type) { | |
| 492 | case MOD_LOAD: | |
| 493 | TASK_INIT(&firmware_unload_task, 0, unloadentry, NULL); | |
| 494 | lockinit(&firmware_lock, "firmware table", 0, LK_CANRECURSE); | |
| 495 | firmware_tq = taskqueue_create("taskqueue_firmware", M_WAITOK, | |
| 496 | taskqueue_thread_enqueue, &firmware_tq); | |
| 497 | /* NB: use our own loop routine that sets up context */ | |
| 498 | (void) taskqueue_start_threads(&firmware_tq, 1, TDPRI_KERN_DAEMON, | |
| 499 | -1, "firmware taskq"); | |
| 500 | if (rootvnode != NULL) { | |
| 501 | /* | |
| 502 | * Root is already mounted so we won't get an event; | |
| 503 | * simulate one here. | |
| 504 | */ | |
| 89e93e6f | 505 | #ifdef notyet |
| 32e913d7 JT |
506 | firmware_mountroot(NULL); |
| 507 | #endif | |
| 508 | } | |
| 509 | return 0; | |
| 510 | ||
| 511 | case MOD_UNLOAD: | |
| 512 | /* request all autoloaded modules to be released */ | |
| 513 | lockmgr(&firmware_lock, LK_EXCLUSIVE); | |
| 514 | for (i = 0; i < FIRMWARE_MAX; i++) { | |
| 515 | fp = &firmware_table[i]; | |
| 516 | fp->flags |= FW_UNLOAD; | |
| 517 | } | |
| 518 | lockmgr(&firmware_lock, LK_RELEASE); | |
| 519 | taskqueue_enqueue(firmware_tq, &firmware_unload_task); | |
| 520 | taskqueue_drain(firmware_tq, &firmware_unload_task); | |
| 521 | err = 0; | |
| 522 | for (i = 0; i < FIRMWARE_MAX; i++) { | |
| 523 | fp = &firmware_table[i]; | |
| 524 | if (fp->fw.name != NULL) { | |
| 525 | kprintf("%s: image %p ref %d still active slot %d\n", | |
| 526 | __func__, fp->fw.name, | |
| 527 | fp->refcnt, i); | |
| 528 | err = EINVAL; | |
| 529 | } | |
| 530 | } | |
| 531 | if (err == 0) | |
| 532 | taskqueue_free(firmware_tq); | |
| 533 | return err; | |
| 534 | } | |
| 535 | return EINVAL; | |
| 536 | } | |
| 537 | ||
| d83c779a SW |
538 | static moduledata_t firmware_mod = { |
| 539 | "firmware", | |
| 32e913d7 JT |
540 | firmware_modevent, |
| 541 | NULL | |
| 542 | }; | |
| d83c779a SW |
543 | DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); |
| 544 | MODULE_VERSION(firmware, 1); |