The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_drvctl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: eb838b900efbf4a83e645eb56c7f1a0a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.