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.19.6.3 2009/05/03 22:39:49 snj 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.19.6.3 2009/05/03 22:39:49 snj 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/malloc.h>
   39 #include <sys/kmem.h>
   40 #include <sys/ioctl.h>
   41 #include <sys/fcntl.h>
   42 #include <sys/file.h>
   43 #include <sys/filedesc.h>
   44 #include <sys/select.h>
   45 #include <sys/poll.h>
   46 #include <sys/drvctlio.h>
   47 #include <sys/devmon.h>
   48 
   49 struct drvctl_event {
   50         TAILQ_ENTRY(drvctl_event) dce_link;
   51         prop_dictionary_t       dce_event;
   52 };
   53 
   54 TAILQ_HEAD(drvctl_queue, drvctl_event);
   55 
   56 static struct drvctl_queue      drvctl_eventq;          /* FIFO */
   57 static kcondvar_t               drvctl_cond;
   58 static kmutex_t                 drvctl_lock;
   59 static int                      drvctl_nopen = 0, drvctl_eventcnt = 0;
   60 static struct selinfo           drvctl_rdsel;
   61 
   62 #define DRVCTL_EVENTQ_DEPTH     64      /* arbitrary queue limit */
   63 
   64 dev_type_open(drvctlopen);
   65 
   66 const struct cdevsw drvctl_cdevsw = {
   67         drvctlopen, nullclose, nullread, nullwrite, noioctl,
   68         nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
   69 };
   70 
   71 void drvctlattach(int);
   72 
   73 static int      drvctl_read(struct file *, off_t *, struct uio *,
   74                             kauth_cred_t, int);
   75 static int      drvctl_write(struct file *, off_t *, struct uio *,
   76                              kauth_cred_t, int);
   77 static int      drvctl_ioctl(struct file *, u_long, void *);
   78 static int      drvctl_poll(struct file *, int);
   79 static int      drvctl_close(struct file *);
   80 
   81 static const struct fileops drvctl_fileops = {
   82         .fo_read = drvctl_read,
   83         .fo_write = drvctl_write,
   84         .fo_ioctl = drvctl_ioctl,
   85         .fo_fcntl = fnullop_fcntl,
   86         .fo_poll = drvctl_poll,
   87         .fo_stat = fbadop_stat,
   88         .fo_close = drvctl_close,
   89         .fo_kqfilter = fnullop_kqfilter,
   90         .fo_drain = fnullop_drain,
   91 };
   92 
   93 #define MAXLOCATORS 100
   94 
   95 static int drvctl_command(struct lwp *, struct plistref *, u_long, int);
   96 static int drvctl_getevent(struct lwp *, struct plistref *, u_long, int);
   97 
   98 void
   99 drvctl_init(void)
  100 {
  101         TAILQ_INIT(&drvctl_eventq);
  102         mutex_init(&drvctl_lock, MUTEX_DEFAULT, IPL_NONE);
  103         cv_init(&drvctl_cond, "devmon");
  104         selinit(&drvctl_rdsel);
  105 }
  106 
  107 void
  108 devmon_insert(const char *event, prop_dictionary_t ev)
  109 {
  110         struct drvctl_event *dce, *odce;;
  111 
  112         mutex_enter(&drvctl_lock);
  113 
  114         if (drvctl_nopen == 0) {
  115                 mutex_exit(&drvctl_lock);
  116                 return;
  117         }
  118 
  119         /* Fill in mandatory member */
  120         if (!prop_dictionary_set_cstring_nocopy(ev, "event", event)) {
  121                 prop_object_release(ev);
  122                 mutex_exit(&drvctl_lock);
  123                 return;
  124         }
  125 
  126         dce = kmem_alloc(sizeof(*dce), KM_SLEEP);
  127         if (dce == NULL) {
  128                 mutex_exit(&drvctl_lock);
  129                 return;
  130         }
  131 
  132         dce->dce_event = ev;
  133 
  134         if (drvctl_eventcnt == DRVCTL_EVENTQ_DEPTH) {
  135                 odce = TAILQ_FIRST(&drvctl_eventq);
  136                 TAILQ_REMOVE(&drvctl_eventq, odce, dce_link);
  137                 prop_object_release(odce->dce_event);
  138                 kmem_free(odce, sizeof(*odce));
  139                 --drvctl_eventcnt;
  140         }
  141 
  142         TAILQ_INSERT_TAIL(&drvctl_eventq, dce, dce_link);
  143         ++drvctl_eventcnt;
  144         cv_broadcast(&drvctl_cond);
  145         selnotify(&drvctl_rdsel, 0, 0);
  146 
  147         mutex_exit(&drvctl_lock);
  148 }
  149 
  150 int
  151 drvctlopen(dev_t dev, int flags, int mode, struct lwp *l)
  152 {
  153         struct file *fp;
  154         int fd;
  155         int ret;
  156 
  157         ret = fd_allocfile(&fp, &fd);
  158         if (ret)
  159                 return (ret);
  160 
  161         /* XXX setup context */
  162         mutex_enter(&drvctl_lock);
  163         ret = fd_clone(fp, fd, flags, &drvctl_fileops, /* context */NULL);
  164         ++drvctl_nopen;
  165         mutex_exit(&drvctl_lock);
  166 
  167         return ret;
  168 }
  169 
  170 static int
  171 pmdevbyname(u_long cmd, struct devpmargs *a)
  172 {
  173         struct device *d;
  174 
  175         if ((d = device_find_by_xname(a->devname)) == NULL)
  176                 return ENXIO;
  177 
  178         switch (cmd) {
  179         case DRVSUSPENDDEV:
  180                 return pmf_device_recursive_suspend(d, PMF_F_NONE) ? 0 : EBUSY;
  181         case DRVRESUMEDEV:
  182                 if (a->flags & DEVPM_F_SUBTREE) {
  183                         return pmf_device_resume_subtree(d, PMF_F_NONE)
  184                             ? 0 : EBUSY;
  185                 } else {
  186                         return pmf_device_recursive_resume(d, PMF_F_NONE)
  187                             ? 0 : EBUSY;
  188                 }
  189         default:
  190                 return EPASSTHROUGH;
  191         }
  192 }
  193 
  194 static int
  195 listdevbyname(struct devlistargs *l)
  196 {
  197         device_t d, child;
  198         deviter_t di;
  199         int cnt = 0, idx, error = 0;
  200 
  201         if (*l->l_devname == '\0')
  202                 d = (device_t)NULL;
  203         else if (memchr(l->l_devname, 0, sizeof(l->l_devname)) == NULL)
  204                 return EINVAL;
  205         else if ((d = device_find_by_xname(l->l_devname)) == NULL)
  206                 return ENXIO;
  207 
  208         for (child = deviter_first(&di, 0); child != NULL;
  209              child = deviter_next(&di)) {
  210                 if (device_parent(child) != d)
  211                         continue;
  212                 idx = cnt++;
  213                 if (l->l_childname == NULL || idx >= l->l_children)
  214                         continue;
  215                 error = copyoutstr(device_xname(child), l->l_childname[idx],
  216                                 sizeof(l->l_childname[idx]), NULL);
  217                 if (error != 0)
  218                         break;
  219         }
  220         deviter_release(&di);
  221 
  222         l->l_children = cnt;
  223         return error;
  224 }
  225 
  226 static int
  227 detachdevbyname(const char *devname)
  228 {
  229         struct device *d;
  230 
  231         if ((d = device_find_by_xname(devname)) == NULL)
  232                 return ENXIO;
  233 
  234 #ifndef XXXFULLRISK
  235         /*
  236          * If the parent cannot be notified, it might keep
  237          * pointers to the detached device.
  238          * There might be a private notification mechanism,
  239          * but better play save here.
  240          */
  241         if (d->dv_parent && !d->dv_parent->dv_cfattach->ca_childdetached)
  242                 return (ENOTSUP);
  243 #endif
  244         return (config_detach(d, 0));
  245 }
  246 
  247 static int
  248 rescanbus(const char *busname, const char *ifattr,
  249           int numlocators, const int *locators)
  250 {
  251         int i, rc;
  252         struct device *d;
  253         const struct cfiattrdata * const *ap;
  254 
  255         /* XXX there should be a way to get limits and defaults (per device)
  256            from config generated data */
  257         int locs[MAXLOCATORS];
  258         for (i = 0; i < MAXLOCATORS; i++)
  259                 locs[i] = -1;
  260 
  261         for (i = 0; i < numlocators;i++)
  262                 locs[i] = locators[i];
  263 
  264         if ((d = device_find_by_xname(busname)) == NULL)
  265                 return ENXIO;
  266 
  267         /*
  268          * must support rescan, and must have something
  269          * to attach to
  270          */
  271         if (!d->dv_cfattach->ca_rescan ||
  272             !d->dv_cfdriver->cd_attrs)
  273                 return (ENODEV);
  274 
  275         /* allow to omit attribute if there is exactly one */
  276         if (!ifattr) {
  277                 if (d->dv_cfdriver->cd_attrs[1])
  278                         return (EINVAL);
  279                 ifattr = d->dv_cfdriver->cd_attrs[0]->ci_name;
  280         } else {
  281                 /* check for valid attribute passed */
  282                 for (ap = d->dv_cfdriver->cd_attrs; *ap; ap++)
  283                         if (!strcmp((*ap)->ci_name, ifattr))
  284                                 break;
  285                 if (!*ap)
  286                         return (EINVAL);
  287         }
  288 
  289         rc = (*d->dv_cfattach->ca_rescan)(d, ifattr, locs);
  290         config_deferred(NULL);
  291         return rc;
  292 }
  293 
  294 static int
  295 drvctl_read(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
  296     int flags)
  297 {
  298         return (ENODEV);
  299 }
  300 
  301 static int
  302 drvctl_write(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
  303     int flags)
  304 {
  305         return (ENODEV);
  306 }
  307 
  308 static int
  309 drvctl_ioctl(struct file *fp, u_long cmd, void *data)
  310 {
  311         int res;
  312         char *ifattr;
  313         int *locs;
  314 
  315         switch (cmd) {
  316         case DRVSUSPENDDEV:
  317         case DRVRESUMEDEV:
  318 #define d ((struct devpmargs *)data)
  319                 res = pmdevbyname(cmd, d);
  320 #undef d
  321                 break;
  322         case DRVLISTDEV:
  323                 res = listdevbyname((struct devlistargs *)data);
  324                 break;
  325         case DRVDETACHDEV:
  326 #define d ((struct devdetachargs *)data)
  327                 res = detachdevbyname(d->devname);
  328 #undef d
  329                 break;
  330         case DRVRESCANBUS:
  331 #define d ((struct devrescanargs *)data)
  332                 d->busname[sizeof(d->busname) - 1] = '\0';
  333 
  334                 /* XXX better copyin? */
  335                 if (d->ifattr[0]) {
  336                         d->ifattr[sizeof(d->ifattr) - 1] = '\0';
  337                         ifattr = d->ifattr;
  338                 } else
  339                         ifattr = 0;
  340 
  341                 if (d->numlocators) {
  342                         if (d->numlocators > MAXLOCATORS)
  343                                 return (EINVAL);
  344                         locs = malloc(d->numlocators * sizeof(int), M_DEVBUF,
  345                                       M_WAITOK);
  346                         res = copyin(d->locators, locs,
  347                                      d->numlocators * sizeof(int));
  348                         if (res) {
  349                                 free(locs, M_DEVBUF);
  350                                 return (res);
  351                         }
  352                 } else
  353                         locs = 0;
  354                 res = rescanbus(d->busname, ifattr, d->numlocators, locs);
  355                 if (locs)
  356                         free(locs, M_DEVBUF);
  357 #undef d
  358                 break;
  359         case DRVCTLCOMMAND:
  360                 res = drvctl_command(curlwp, (struct plistref *)data, cmd,
  361                     fp->f_flag);
  362                 break;
  363         case DRVGETEVENT:
  364                 res = drvctl_getevent(curlwp, (struct plistref *)data, cmd,
  365                     fp->f_flag);
  366                 break;
  367         default:
  368                 return (EPASSTHROUGH);
  369         }
  370         return (res);
  371 }
  372 
  373 static int
  374 drvctl_poll(struct file *fp, int events)
  375 {
  376         int revents = 0;
  377 
  378         if (!TAILQ_EMPTY(&drvctl_eventq))
  379                 revents |= events & (POLLIN | POLLRDNORM);
  380         else
  381                 selrecord(curlwp, &drvctl_rdsel);
  382 
  383         return revents;
  384 }
  385 
  386 static int
  387 drvctl_close(struct file *fp)
  388 {
  389         struct drvctl_event *dce;
  390 
  391         /* XXX free context */
  392         mutex_enter(&drvctl_lock);
  393         KASSERT(drvctl_nopen > 0);
  394         --drvctl_nopen;
  395         if (drvctl_nopen == 0) {
  396                 /* flush queue */
  397                 while ((dce = TAILQ_FIRST(&drvctl_eventq)) != NULL) {
  398                         TAILQ_REMOVE(&drvctl_eventq, dce, dce_link);
  399                         KASSERT(drvctl_eventcnt > 0);
  400                         --drvctl_eventcnt;
  401                         prop_object_release(dce->dce_event);
  402                         kmem_free(dce, sizeof(*dce));
  403                 }
  404         }
  405         mutex_exit(&drvctl_lock);
  406 
  407         return (0);
  408 }
  409 
  410 void
  411 drvctlattach(int arg)
  412 {
  413 }
  414 
  415 /*****************************************************************************
  416  * Driver control command processing engine
  417  *****************************************************************************/
  418 
  419 static int
  420 drvctl_command_get_properties(struct lwp *l,
  421                               prop_dictionary_t command_dict,
  422                               prop_dictionary_t results_dict)
  423 {
  424         prop_dictionary_t args_dict;
  425         prop_string_t devname_string;
  426         device_t dev;
  427         deviter_t di;
  428         
  429         args_dict = prop_dictionary_get(command_dict, "drvctl-arguments");
  430         if (args_dict == NULL)
  431                 return (EINVAL);
  432 
  433         devname_string = prop_dictionary_get(args_dict, "device-name");
  434         if (devname_string == NULL)
  435                 return (EINVAL);
  436         
  437         for (dev = deviter_first(&di, 0); dev != NULL;
  438              dev = deviter_next(&di)) {
  439                 if (prop_string_equals_cstring(devname_string,
  440                                                device_xname(dev))) {
  441                         prop_dictionary_set(results_dict, "drvctl-result-data",
  442                             device_properties(dev));
  443                         break;
  444                 }
  445         }
  446 
  447         deviter_release(&di);
  448 
  449         if (dev == NULL)
  450                 return (ESRCH);
  451 
  452         return (0);
  453 }
  454 
  455 struct drvctl_command_desc {
  456         const char *dcd_name;           /* command name */
  457         int (*dcd_func)(struct lwp *,   /* handler function */
  458                         prop_dictionary_t,
  459                         prop_dictionary_t);
  460         int dcd_rw;                     /* read or write required */
  461 };
  462 
  463 static const struct drvctl_command_desc drvctl_command_table[] = {
  464         { .dcd_name = "get-properties",
  465           .dcd_func = drvctl_command_get_properties,
  466           .dcd_rw   = FREAD,
  467         },
  468 
  469         { .dcd_name = NULL }
  470 };
  471 
  472 static int
  473 drvctl_command(struct lwp *l, struct plistref *pref, u_long ioctl_cmd,
  474                int fflag)
  475 {
  476         prop_dictionary_t command_dict, results_dict;
  477         prop_string_t command_string;
  478         const struct drvctl_command_desc *dcd;
  479         int error;
  480 
  481         error = prop_dictionary_copyin_ioctl(pref, ioctl_cmd, &command_dict);
  482         if (error)
  483                 return (error);
  484 
  485         results_dict = prop_dictionary_create();
  486         if (results_dict == NULL) {
  487                 prop_object_release(command_dict);
  488                 return (ENOMEM);
  489         }
  490         
  491         command_string = prop_dictionary_get(command_dict, "drvctl-command");
  492         if (command_string == NULL) {
  493                 error = EINVAL;
  494                 goto out;
  495         }
  496 
  497         for (dcd = drvctl_command_table; dcd->dcd_name != NULL; dcd++) {
  498                 if (prop_string_equals_cstring(command_string,
  499                                                dcd->dcd_name))
  500                         break;
  501         }
  502 
  503         if (dcd->dcd_name == NULL) {
  504                 error = EINVAL;
  505                 goto out;
  506         }
  507 
  508         if ((fflag & dcd->dcd_rw) == 0) {
  509                 error = EPERM;
  510                 goto out;
  511         }
  512 
  513         error = (*dcd->dcd_func)(l, command_dict, results_dict);
  514 
  515         prop_dictionary_set_int32(results_dict, "drvctl-error", error);
  516 
  517         error = prop_dictionary_copyout_ioctl(pref, ioctl_cmd, results_dict);
  518  out:
  519         prop_object_release(command_dict);
  520         prop_object_release(results_dict);
  521         return (error);
  522 }
  523 
  524 static int
  525 drvctl_getevent(struct lwp *l, struct plistref *pref, u_long ioctl_cmd,
  526                 int fflag)
  527 {
  528         struct drvctl_event *dce;
  529         int ret;
  530 
  531         if ((fflag & (FREAD|FWRITE)) != (FREAD|FWRITE))
  532                 return (EPERM);
  533 
  534         mutex_enter(&drvctl_lock);
  535         while ((dce = TAILQ_FIRST(&drvctl_eventq)) == NULL) {
  536                 if (fflag & O_NONBLOCK) {
  537                         mutex_exit(&drvctl_lock);
  538                         return (EWOULDBLOCK);
  539                 }
  540 
  541                 ret = cv_wait_sig(&drvctl_cond, &drvctl_lock);
  542                 if (ret) {
  543                         mutex_exit(&drvctl_lock);
  544                         return (ret);
  545                 }
  546         }
  547         TAILQ_REMOVE(&drvctl_eventq, dce, dce_link);
  548         KASSERT(drvctl_eventcnt > 0);
  549         --drvctl_eventcnt;
  550         mutex_exit(&drvctl_lock);
  551 
  552         ret = prop_dictionary_copyout_ioctl(pref, ioctl_cmd, dce->dce_event);
  553 
  554         prop_object_release(dce->dce_event);
  555         kmem_free(dce, sizeof(*dce));
  556 
  557         return (ret);
  558 }

Cache object: 167e53c4ff4313415585841ca9af1e45


[ 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.