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/dev/evdev/cdev.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 /*-
    2  * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org>
    3  * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org>
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following 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 AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * $FreeBSD$
   28  */
   29 
   30 #include "opt_evdev.h"
   31 
   32 #include <sys/param.h>
   33 #include <sys/bitstring.h>
   34 #include <sys/conf.h>
   35 #include <sys/epoch.h>
   36 #include <sys/filio.h>
   37 #include <sys/fcntl.h>
   38 #include <sys/kernel.h>
   39 #include <sys/malloc.h>
   40 #include <sys/poll.h>
   41 #include <sys/proc.h>
   42 #include <sys/selinfo.h>
   43 #include <sys/systm.h>
   44 #include <sys/time.h>
   45 #include <sys/uio.h>
   46 
   47 #include <dev/evdev/evdev.h>
   48 #include <dev/evdev/evdev_private.h>
   49 #include <dev/evdev/input.h>
   50 
   51 #ifdef COMPAT_FREEBSD32
   52 #include <sys/mount.h>
   53 #include <sys/sysent.h>
   54 #include <compat/freebsd32/freebsd32.h>
   55 struct input_event32 {
   56         struct timeval32        time;
   57         uint16_t                type;
   58         uint16_t                code;
   59         int32_t                 value;
   60 };
   61 #endif
   62 
   63 #ifdef EVDEV_DEBUG
   64 #define debugf(client, fmt, args...)    printf("evdev cdev: "fmt"\n", ##args)
   65 #else
   66 #define debugf(client, fmt, args...)
   67 #endif
   68 
   69 #define DEF_RING_REPORTS        8
   70 
   71 static d_open_t         evdev_open;
   72 static d_read_t         evdev_read;
   73 static d_write_t        evdev_write;
   74 static d_ioctl_t        evdev_ioctl;
   75 static d_poll_t         evdev_poll;
   76 static d_kqfilter_t     evdev_kqfilter;
   77 
   78 static int evdev_kqread(struct knote *kn, long hint);
   79 static void evdev_kqdetach(struct knote *kn);
   80 static void evdev_dtor(void *);
   81 static int evdev_ioctl_eviocgbit(struct evdev_dev *, int, int, caddr_t,
   82     struct thread *);
   83 static void evdev_client_filter_queue(struct evdev_client *, uint16_t);
   84 
   85 static struct cdevsw evdev_cdevsw = {
   86         .d_version = D_VERSION,
   87         .d_open = evdev_open,
   88         .d_read = evdev_read,
   89         .d_write = evdev_write,
   90         .d_ioctl = evdev_ioctl,
   91         .d_poll = evdev_poll,
   92         .d_kqfilter = evdev_kqfilter,
   93         .d_name = "evdev",
   94 };
   95 
   96 static struct filterops evdev_cdev_filterops = {
   97         .f_isfd = 1,
   98         .f_attach = NULL,
   99         .f_detach = evdev_kqdetach,
  100         .f_event = evdev_kqread,
  101 };
  102 
  103 static int
  104 evdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
  105 {
  106         struct evdev_dev *evdev = dev->si_drv1;
  107         struct evdev_client *client;
  108         size_t buffer_size;
  109         int ret;
  110 
  111         if (evdev == NULL)
  112                 return (ENODEV);
  113 
  114         /* Initialize client structure */
  115         buffer_size = evdev->ev_report_size * DEF_RING_REPORTS;
  116         client = malloc(offsetof(struct evdev_client, ec_buffer) +
  117             sizeof(struct input_event) * buffer_size,
  118             M_EVDEV, M_WAITOK | M_ZERO);
  119 
  120         /* Initialize ring buffer */
  121         client->ec_buffer_size = buffer_size;
  122         client->ec_buffer_head = 0;
  123         client->ec_buffer_tail = 0;
  124         client->ec_buffer_ready = 0;
  125 
  126         client->ec_evdev = evdev;
  127         mtx_init(&client->ec_buffer_mtx, "evclient", "evdev", MTX_DEF);
  128         knlist_init_mtx(&client->ec_selp.si_note, &client->ec_buffer_mtx);
  129 
  130         ret = EVDEV_LIST_LOCK_SIG(evdev);
  131         if (ret != 0)
  132                 goto out;
  133         /* Avoid race with evdev_unregister */
  134         if (dev->si_drv1 == NULL)
  135                 ret = ENODEV;
  136         else
  137                 ret = evdev_register_client(evdev, client);
  138         EVDEV_LIST_UNLOCK(evdev);
  139 out:
  140         if (ret == 0)
  141                 ret = devfs_set_cdevpriv(client, evdev_dtor);
  142         else
  143                 client->ec_revoked = true;
  144 
  145         if (ret != 0) {
  146                 debugf(client, "cannot register evdev client");
  147                 evdev_dtor(client);
  148         }
  149 
  150         return (ret);
  151 }
  152 
  153 static void
  154 evdev_dtor(void *data)
  155 {
  156         struct evdev_client *client = (struct evdev_client *)data;
  157 
  158         EVDEV_LIST_LOCK(client->ec_evdev);
  159         if (!client->ec_revoked)
  160                 evdev_dispose_client(client->ec_evdev, client);
  161         EVDEV_LIST_UNLOCK(client->ec_evdev);
  162 
  163         if (client->ec_evdev->ev_lock_type != EV_LOCK_MTX)
  164                 epoch_wait_preempt(INPUT_EPOCH);
  165         knlist_clear(&client->ec_selp.si_note, 0);
  166         seldrain(&client->ec_selp);
  167         knlist_destroy(&client->ec_selp.si_note);
  168         funsetown(&client->ec_sigio);
  169         mtx_destroy(&client->ec_buffer_mtx);
  170         free(client, M_EVDEV);
  171 }
  172 
  173 static int
  174 evdev_read(struct cdev *dev, struct uio *uio, int ioflag)
  175 {
  176         struct evdev_client *client;
  177         union {
  178                 struct input_event t;
  179 #ifdef COMPAT_FREEBSD32
  180                 struct input_event32 t32;
  181 #endif
  182         } event;
  183         struct input_event *head;
  184         size_t evsize;
  185         int ret = 0;
  186         int remaining;
  187 
  188         ret = devfs_get_cdevpriv((void **)&client);
  189         if (ret != 0)
  190                 return (ret);
  191 
  192         debugf(client, "read %zd bytes by thread %d", uio->uio_resid,
  193             uio->uio_td->td_tid);
  194 
  195         if (client->ec_revoked)
  196                 return (ENODEV);
  197 
  198 #ifdef COMPAT_FREEBSD32
  199         if (SV_CURPROC_FLAG(SV_ILP32))
  200                 evsize = sizeof(struct input_event32);
  201         else
  202 #endif
  203                 evsize = sizeof(struct input_event);
  204 
  205         /* Zero-sized reads are allowed for error checking */
  206         if (uio->uio_resid != 0 && uio->uio_resid < evsize)
  207                 return (EINVAL);
  208 
  209         remaining = uio->uio_resid / evsize;
  210 
  211         EVDEV_CLIENT_LOCKQ(client);
  212 
  213         if (EVDEV_CLIENT_EMPTYQ(client)) {
  214                 if (ioflag & O_NONBLOCK)
  215                         ret = EWOULDBLOCK;
  216                 else {
  217                         if (remaining != 0) {
  218                                 client->ec_blocked = true;
  219                                 ret = mtx_sleep(client, &client->ec_buffer_mtx,
  220                                     PCATCH, "evread", 0);
  221                                 if (ret == 0 && client->ec_revoked)
  222                                         ret = ENODEV;
  223                         }
  224                 }
  225         }
  226 
  227         while (ret == 0 && !EVDEV_CLIENT_EMPTYQ(client) && remaining > 0) {
  228                 head = client->ec_buffer + client->ec_buffer_head;
  229 #ifdef COMPAT_FREEBSD32
  230                 if (SV_CURPROC_FLAG(SV_ILP32)) {
  231                         bzero(&event.t32, sizeof(struct input_event32));
  232                         TV_CP(*head, event.t32, time);
  233                         CP(*head, event.t32, type);
  234                         CP(*head, event.t32, code);
  235                         CP(*head, event.t32, value);
  236                 } else
  237 #endif
  238                         bcopy(head, &event.t, evsize);
  239 
  240                 client->ec_buffer_head =
  241                     (client->ec_buffer_head + 1) % client->ec_buffer_size;
  242                 remaining--;
  243 
  244                 EVDEV_CLIENT_UNLOCKQ(client);
  245                 ret = uiomove(&event, evsize, uio);
  246                 EVDEV_CLIENT_LOCKQ(client);
  247         }
  248 
  249         EVDEV_CLIENT_UNLOCKQ(client);
  250 
  251         return (ret);
  252 }
  253 
  254 static int
  255 evdev_write(struct cdev *dev, struct uio *uio, int ioflag)
  256 {
  257         struct evdev_dev *evdev = dev->si_drv1;
  258         struct evdev_client *client;
  259         union {
  260                 struct input_event t;
  261 #ifdef COMPAT_FREEBSD32
  262                 struct input_event32 t32;
  263 #endif
  264         } event;
  265         size_t evsize;
  266         int ret = 0;
  267 
  268         ret = devfs_get_cdevpriv((void **)&client);
  269         if (ret != 0)
  270                 return (ret);
  271 
  272         debugf(client, "write %zd bytes by thread %d", uio->uio_resid,
  273             uio->uio_td->td_tid);
  274 
  275         if (client->ec_revoked || evdev == NULL)
  276                 return (ENODEV);
  277 
  278 #ifdef COMPAT_FREEBSD32
  279         if (SV_CURPROC_FLAG(SV_ILP32))
  280                 evsize = sizeof(struct input_event32);
  281         else
  282 #endif
  283                 evsize = sizeof(struct input_event);
  284 
  285         if (uio->uio_resid % evsize != 0) {
  286                 debugf(client, "write size not multiple of input_event size");
  287                 return (EINVAL);
  288         }
  289 
  290         while (uio->uio_resid > 0 && ret == 0) {
  291                 ret = uiomove(&event, evsize, uio);
  292                 if (ret == 0) {
  293 #ifdef COMPAT_FREEBSD32
  294                         if (SV_CURPROC_FLAG(SV_ILP32))
  295                                 ret = evdev_inject_event(evdev, event.t32.type,
  296                                     event.t32.code, event.t32.value);
  297                         else
  298 #endif
  299                                 ret = evdev_inject_event(evdev, event.t.type,
  300                                     event.t.code, event.t.value);
  301                 }
  302         }
  303 
  304         return (ret);
  305 }
  306 
  307 static int
  308 evdev_poll(struct cdev *dev, int events, struct thread *td)
  309 {
  310         struct evdev_client *client;
  311         int ret;
  312         int revents = 0;
  313 
  314         ret = devfs_get_cdevpriv((void **)&client);
  315         if (ret != 0)
  316                 return (POLLNVAL);
  317 
  318         debugf(client, "poll by thread %d", td->td_tid);
  319 
  320         if (client->ec_revoked)
  321                 return (POLLHUP);
  322 
  323         if (events & (POLLIN | POLLRDNORM)) {
  324                 EVDEV_CLIENT_LOCKQ(client);
  325                 if (!EVDEV_CLIENT_EMPTYQ(client))
  326                         revents = events & (POLLIN | POLLRDNORM);
  327                 else {
  328                         client->ec_selected = true;
  329                         selrecord(td, &client->ec_selp);
  330                 }
  331                 EVDEV_CLIENT_UNLOCKQ(client);
  332         }
  333 
  334         return (revents);
  335 }
  336 
  337 static int
  338 evdev_kqfilter(struct cdev *dev, struct knote *kn)
  339 {
  340         struct evdev_client *client;
  341         int ret;
  342 
  343         ret = devfs_get_cdevpriv((void **)&client);
  344         if (ret != 0)
  345                 return (ret);
  346 
  347         if (client->ec_revoked)
  348                 return (ENODEV);
  349 
  350         switch(kn->kn_filter) {
  351         case EVFILT_READ:
  352                 kn->kn_fop = &evdev_cdev_filterops;
  353                 break;
  354         default:
  355                 return(EINVAL);
  356         }
  357         kn->kn_hook = (caddr_t)client;
  358 
  359         knlist_add(&client->ec_selp.si_note, kn, 0);
  360         return (0);
  361 }
  362 
  363 static int
  364 evdev_kqread(struct knote *kn, long hint)
  365 {
  366         struct evdev_client *client;
  367         int ret;
  368 
  369         client = (struct evdev_client *)kn->kn_hook;
  370 
  371         EVDEV_CLIENT_LOCKQ_ASSERT(client);
  372 
  373         if (client->ec_revoked) {
  374                 kn->kn_flags |= EV_EOF;
  375                 ret = 1;
  376         } else {
  377                 kn->kn_data = EVDEV_CLIENT_SIZEQ(client) *
  378                     sizeof(struct input_event);
  379                 ret = !EVDEV_CLIENT_EMPTYQ(client);
  380         }
  381         return (ret);
  382 }
  383 
  384 static void
  385 evdev_kqdetach(struct knote *kn)
  386 {
  387         struct evdev_client *client;
  388 
  389         client = (struct evdev_client *)kn->kn_hook;
  390         knlist_remove(&client->ec_selp.si_note, kn, 0);
  391 }
  392 
  393 static int
  394 evdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
  395     struct thread *td)
  396 {
  397         struct evdev_dev *evdev = dev->si_drv1;
  398         struct evdev_client *client;
  399         struct input_keymap_entry *ke;
  400         struct epoch_tracker et;
  401         int ret, len, limit, type_num;
  402         uint32_t code;
  403         size_t nvalues;
  404 
  405         ret = devfs_get_cdevpriv((void **)&client);
  406         if (ret != 0)
  407                 return (ret);
  408 
  409         if (client->ec_revoked || evdev == NULL)
  410                 return (ENODEV);
  411 
  412         /*
  413          * Fix evdev state corrupted with discarding of kdb events.
  414          * EVIOCGKEY and EVIOCGLED ioctls can suffer from this.
  415          */
  416         if (evdev->ev_kdb_active) {
  417                 EVDEV_LOCK(evdev);
  418                 if (evdev->ev_kdb_active) {
  419                         evdev->ev_kdb_active = false;
  420                         if (evdev->ev_lock_type == EV_LOCK_EXT_EPOCH)
  421                                 epoch_enter_preempt(INPUT_EPOCH, &et);
  422                         evdev_restore_after_kdb(evdev);
  423                         if (evdev->ev_lock_type == EV_LOCK_EXT_EPOCH)
  424                                 epoch_exit_preempt(INPUT_EPOCH, &et);
  425                 }
  426                 EVDEV_UNLOCK(evdev);
  427         }
  428 
  429         /* file I/O ioctl handling */
  430         switch (cmd) {
  431         case FIOSETOWN:
  432                 return (fsetown(*(int *)data, &client->ec_sigio));
  433 
  434         case FIOGETOWN:
  435                 *(int *)data = fgetown(&client->ec_sigio);
  436                 return (0);
  437 
  438         case FIONBIO:
  439                 return (0);
  440 
  441         case FIOASYNC:
  442                 if (*(int *)data)
  443                         client->ec_async = true;
  444                 else
  445                         client->ec_async = false;
  446 
  447                 return (0);
  448 
  449         case FIONREAD:
  450                 EVDEV_CLIENT_LOCKQ(client);
  451                 *(int *)data =
  452                     EVDEV_CLIENT_SIZEQ(client) * sizeof(struct input_event);
  453                 EVDEV_CLIENT_UNLOCKQ(client);
  454                 return (0);
  455         }
  456 
  457         len = IOCPARM_LEN(cmd);
  458         debugf(client, "ioctl called: cmd=0x%08lx, data=%p", cmd, data);
  459 
  460         /* evdev fixed-length ioctls handling */
  461         switch (cmd) {
  462         case EVIOCGVERSION:
  463                 *(int *)data = EV_VERSION;
  464                 return (0);
  465 
  466         case EVIOCGID:
  467                 debugf(client, "EVIOCGID: bus=%d vendor=0x%04x product=0x%04x",
  468                     evdev->ev_id.bustype, evdev->ev_id.vendor,
  469                     evdev->ev_id.product);
  470                 memcpy(data, &evdev->ev_id, sizeof(struct input_id));
  471                 return (0);
  472 
  473         case EVIOCGREP:
  474                 if (!evdev_event_supported(evdev, EV_REP))
  475                         return (ENOTSUP);
  476 
  477                 memcpy(data, evdev->ev_rep, sizeof(evdev->ev_rep));
  478                 return (0);
  479 
  480         case EVIOCSREP:
  481                 if (!evdev_event_supported(evdev, EV_REP))
  482                         return (ENOTSUP);
  483 
  484                 evdev_inject_event(evdev, EV_REP, REP_DELAY, ((int *)data)[0]);
  485                 evdev_inject_event(evdev, EV_REP, REP_PERIOD,
  486                     ((int *)data)[1]);
  487                 return (0);
  488 
  489         case EVIOCGKEYCODE:
  490                 /* Fake unsupported ioctl */
  491                 return (0);
  492 
  493         case EVIOCGKEYCODE_V2:
  494                 if (evdev->ev_methods == NULL ||
  495                     evdev->ev_methods->ev_get_keycode == NULL)
  496                         return (ENOTSUP);
  497 
  498                 ke = (struct input_keymap_entry *)data;
  499                 evdev->ev_methods->ev_get_keycode(evdev, ke);
  500                 return (0);
  501 
  502         case EVIOCSKEYCODE:
  503                 /* Fake unsupported ioctl */
  504                 return (0);
  505 
  506         case EVIOCSKEYCODE_V2:
  507                 if (evdev->ev_methods == NULL ||
  508                     evdev->ev_methods->ev_set_keycode == NULL)
  509                         return (ENOTSUP);
  510 
  511                 ke = (struct input_keymap_entry *)data;
  512                 evdev->ev_methods->ev_set_keycode(evdev, ke);
  513                 return (0);
  514 
  515         case EVIOCGABS(0) ... EVIOCGABS(ABS_MAX):
  516                 if (evdev->ev_absinfo == NULL)
  517                         return (EINVAL);
  518 
  519                 memcpy(data, &evdev->ev_absinfo[cmd - EVIOCGABS(0)],
  520                     sizeof(struct input_absinfo));
  521                 return (0);
  522 
  523         case EVIOCSABS(0) ... EVIOCSABS(ABS_MAX):
  524                 if (evdev->ev_absinfo == NULL)
  525                         return (EINVAL);
  526 
  527                 code = cmd - EVIOCSABS(0);
  528                 /* mt-slot number can not be changed */
  529                 if (code == ABS_MT_SLOT)
  530                         return (EINVAL);
  531 
  532                 EVDEV_LOCK(evdev);
  533                 evdev_set_absinfo(evdev, code, (struct input_absinfo *)data);
  534                 EVDEV_UNLOCK(evdev);
  535                 return (0);
  536 
  537         case EVIOCSFF:
  538         case EVIOCRMFF:
  539         case EVIOCGEFFECTS:
  540                 /* Fake unsupported ioctls */
  541                 return (0);
  542 
  543         case EVIOCGRAB:
  544                 EVDEV_LOCK(evdev);
  545                 if (*(int *)data)
  546                         ret = evdev_grab_client(evdev, client);
  547                 else
  548                         ret = evdev_release_client(evdev, client);
  549                 EVDEV_UNLOCK(evdev);
  550                 return (ret);
  551 
  552         case EVIOCREVOKE:
  553                 if (*(int *)data != 0)
  554                         return (EINVAL);
  555 
  556                 EVDEV_LIST_LOCK(evdev);
  557                 if (dev->si_drv1 != NULL && !client->ec_revoked) {
  558                         evdev_dispose_client(evdev, client);
  559                         evdev_revoke_client(client);
  560                 }
  561                 EVDEV_LIST_UNLOCK(evdev);
  562                 return (0);
  563 
  564         case EVIOCSCLOCKID:
  565                 switch (*(int *)data) {
  566                 case CLOCK_REALTIME:
  567                         client->ec_clock_id = EV_CLOCK_REALTIME;
  568                         return (0);
  569                 case CLOCK_MONOTONIC:
  570                         client->ec_clock_id = EV_CLOCK_MONOTONIC;
  571                         return (0);
  572                 default:
  573                         return (EINVAL);
  574                 }
  575         }
  576 
  577         /* evdev variable-length ioctls handling */
  578         switch (IOCBASECMD(cmd)) {
  579         case EVIOCGNAME(0):
  580                 /* Linux evdev does not terminate truncated strings with 0 */
  581                 limit = MIN(strlen(evdev->ev_name) + 1, len);
  582                 memcpy(data, evdev->ev_name, limit);
  583                 td->td_retval[0] = limit;
  584                 return (0);
  585 
  586         case EVIOCGPHYS(0):
  587                 if (evdev->ev_shortname[0] == 0)
  588                         return (ENOENT);
  589 
  590                 limit = MIN(strlen(evdev->ev_shortname) + 1, len);
  591                 memcpy(data, evdev->ev_shortname, limit);
  592                 td->td_retval[0] = limit;
  593                 return (0);
  594 
  595         case EVIOCGUNIQ(0):
  596                 if (evdev->ev_serial[0] == 0)
  597                         return (ENOENT);
  598 
  599                 limit = MIN(strlen(evdev->ev_serial) + 1, len);
  600                 memcpy(data, evdev->ev_serial, limit);
  601                 td->td_retval[0] = limit;
  602                 return (0);
  603 
  604         case EVIOCGPROP(0):
  605                 limit = MIN(len, bitstr_size(INPUT_PROP_CNT));
  606                 memcpy(data, evdev->ev_prop_flags, limit);
  607                 td->td_retval[0] = limit;
  608                 return (0);
  609 
  610         case EVIOCGMTSLOTS(0):
  611                 /* EVIOCGMTSLOTS always returns 0 on success */
  612                 if (evdev->ev_mt == NULL)
  613                         return (EINVAL);
  614                 if (len < sizeof(uint32_t))
  615                         return (EINVAL);
  616                 code = *(uint32_t *)data;
  617                 if (!ABS_IS_MT(code))
  618                         return (EINVAL);
  619 
  620                 nvalues =
  621                     MIN(len / sizeof(int32_t) - 1, MAXIMAL_MT_SLOT(evdev) + 1);
  622                 for (int i = 0; i < nvalues; i++)
  623                         ((int32_t *)data)[i + 1] =
  624                             evdev_get_mt_value(evdev, i, code);
  625                 return (0);
  626 
  627         case EVIOCGKEY(0):
  628                 limit = MIN(len, bitstr_size(KEY_CNT));
  629                 EVDEV_LOCK(evdev);
  630                 evdev_client_filter_queue(client, EV_KEY);
  631                 memcpy(data, evdev->ev_key_states, limit);
  632                 EVDEV_UNLOCK(evdev);
  633                 td->td_retval[0] = limit;
  634                 return (0);
  635 
  636         case EVIOCGLED(0):
  637                 limit = MIN(len, bitstr_size(LED_CNT));
  638                 EVDEV_LOCK(evdev);
  639                 evdev_client_filter_queue(client, EV_LED);
  640                 memcpy(data, evdev->ev_led_states, limit);
  641                 EVDEV_UNLOCK(evdev);
  642                 td->td_retval[0] = limit;
  643                 return (0);
  644 
  645         case EVIOCGSND(0):
  646                 limit = MIN(len, bitstr_size(SND_CNT));
  647                 EVDEV_LOCK(evdev);
  648                 evdev_client_filter_queue(client, EV_SND);
  649                 memcpy(data, evdev->ev_snd_states, limit);
  650                 EVDEV_UNLOCK(evdev);
  651                 td->td_retval[0] = limit;
  652                 return (0);
  653 
  654         case EVIOCGSW(0):
  655                 limit = MIN(len, bitstr_size(SW_CNT));
  656                 EVDEV_LOCK(evdev);
  657                 evdev_client_filter_queue(client, EV_SW);
  658                 memcpy(data, evdev->ev_sw_states, limit);
  659                 EVDEV_UNLOCK(evdev);
  660                 td->td_retval[0] = limit;
  661                 return (0);
  662 
  663         case EVIOCGBIT(0, 0) ... EVIOCGBIT(EV_MAX, 0):
  664                 type_num = IOCBASECMD(cmd) - EVIOCGBIT(0, 0);
  665                 debugf(client, "EVIOCGBIT(%d): data=%p, len=%d", type_num,
  666                     data, len);
  667                 return (evdev_ioctl_eviocgbit(evdev, type_num, len, data, td));
  668         }
  669 
  670         return (EINVAL);
  671 }
  672 
  673 static int
  674 evdev_ioctl_eviocgbit(struct evdev_dev *evdev, int type, int len, caddr_t data,
  675     struct thread *td)
  676 {
  677         unsigned long *bitmap;
  678         int limit;
  679 
  680         switch (type) {
  681         case 0:
  682                 bitmap = evdev->ev_type_flags;
  683                 limit = EV_CNT;
  684                 break;
  685         case EV_KEY:
  686                 bitmap = evdev->ev_key_flags;
  687                 limit = KEY_CNT;
  688                 break;
  689         case EV_REL:
  690                 bitmap = evdev->ev_rel_flags;
  691                 limit = REL_CNT;
  692                 break;
  693         case EV_ABS:
  694                 bitmap = evdev->ev_abs_flags;
  695                 limit = ABS_CNT;
  696                 break;
  697         case EV_MSC:
  698                 bitmap = evdev->ev_msc_flags;
  699                 limit = MSC_CNT;
  700                 break;
  701         case EV_LED:
  702                 bitmap = evdev->ev_led_flags;
  703                 limit = LED_CNT;
  704                 break;
  705         case EV_SND:
  706                 bitmap = evdev->ev_snd_flags;
  707                 limit = SND_CNT;
  708                 break;
  709         case EV_SW:
  710                 bitmap = evdev->ev_sw_flags;
  711                 limit = SW_CNT;
  712                 break;
  713         case EV_FF:
  714                 /*
  715                  * We don't support EV_FF now, so let's
  716                  * just fake it returning only zeros.
  717                  */
  718                 bzero(data, len);
  719                 td->td_retval[0] = len;
  720                 return (0);
  721         default:
  722                 return (ENOTTY);
  723         }
  724 
  725         /*
  726          * Clear ioctl data buffer in case it's bigger than
  727          * bitmap size
  728          */
  729         bzero(data, len);
  730 
  731         limit = bitstr_size(limit);
  732         len = MIN(limit, len);
  733         memcpy(data, bitmap, len);
  734         td->td_retval[0] = len;
  735         return (0);
  736 }
  737 
  738 void
  739 evdev_revoke_client(struct evdev_client *client)
  740 {
  741 
  742         EVDEV_LIST_LOCK_ASSERT(client->ec_evdev);
  743 
  744         client->ec_revoked = true;
  745 }
  746 
  747 void
  748 evdev_notify_event(struct evdev_client *client)
  749 {
  750 
  751         EVDEV_CLIENT_LOCKQ_ASSERT(client);
  752 
  753         if (client->ec_blocked) {
  754                 client->ec_blocked = false;
  755                 wakeup(client);
  756         }
  757         if (client->ec_selected) {
  758                 client->ec_selected = false;
  759                 selwakeup(&client->ec_selp);
  760         }
  761         KNOTE_LOCKED(&client->ec_selp.si_note, 0);
  762 
  763         if (client->ec_async && client->ec_sigio != NULL)
  764                 pgsigio(&client->ec_sigio, SIGIO, 0);
  765 }
  766 
  767 int
  768 evdev_cdev_create(struct evdev_dev *evdev)
  769 {
  770         struct make_dev_args mda;
  771         int ret, unit = 0;
  772 
  773         make_dev_args_init(&mda);
  774         mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
  775         mda.mda_devsw = &evdev_cdevsw;
  776         mda.mda_uid = UID_ROOT;
  777         mda.mda_gid = GID_WHEEL;
  778         mda.mda_mode = 0600;
  779         mda.mda_si_drv1 = evdev;
  780 
  781         /* Try to coexist with cuse-backed input/event devices */
  782         while ((ret = make_dev_s(&mda, &evdev->ev_cdev, "input/event%d", unit))
  783             == EEXIST)
  784                 unit++;
  785 
  786         if (ret == 0)
  787                 evdev->ev_unit = unit;
  788 
  789         return (ret);
  790 }
  791 
  792 int
  793 evdev_cdev_destroy(struct evdev_dev *evdev)
  794 {
  795 
  796         destroy_dev(evdev->ev_cdev);
  797         return (0);
  798 }
  799 
  800 static void
  801 evdev_client_gettime(struct evdev_client *client, struct timeval *tv)
  802 {
  803 
  804         switch (client->ec_clock_id) {
  805         case EV_CLOCK_BOOTTIME:
  806                 /*
  807                  * XXX: FreeBSD does not support true POSIX monotonic clock.
  808                  *      So aliase EV_CLOCK_BOOTTIME to EV_CLOCK_MONOTONIC.
  809                  */
  810         case EV_CLOCK_MONOTONIC:
  811                 microuptime(tv);
  812                 break;
  813 
  814         case EV_CLOCK_REALTIME:
  815         default:
  816                 microtime(tv);
  817                 break;
  818         }
  819 }
  820 
  821 void
  822 evdev_client_push(struct evdev_client *client, uint16_t type, uint16_t code,
  823     int32_t value)
  824 {
  825         struct timeval time;
  826         size_t count, head, tail, ready;
  827 
  828         EVDEV_CLIENT_LOCKQ_ASSERT(client);
  829         head = client->ec_buffer_head;
  830         tail = client->ec_buffer_tail;
  831         ready = client->ec_buffer_ready;
  832         count = client->ec_buffer_size;
  833 
  834         /* If queue is full drop its content and place SYN_DROPPED event */
  835         if ((tail + 1) % count == head) {
  836                 debugf(client, "client %p: buffer overflow", client);
  837 
  838                 head = (tail + count - 1) % count;
  839                 client->ec_buffer[head] = (struct input_event) {
  840                         .type = EV_SYN,
  841                         .code = SYN_DROPPED,
  842                         .value = 0
  843                 };
  844                 /*
  845                  * XXX: Here is a small race window from now till the end of
  846                  *      report. The queue is empty but client has been already
  847                  *      notified of data readyness. Can be fixed in two ways:
  848                  * 1. Implement bulk insert so queue lock would not be dropped
  849                  *    till the SYN_REPORT event.
  850                  * 2. Insert SYN_REPORT just now and skip remaining events
  851                  */
  852                 client->ec_buffer_head = head;
  853                 client->ec_buffer_ready = head;
  854         }
  855 
  856         client->ec_buffer[tail].type = type;
  857         client->ec_buffer[tail].code = code;
  858         client->ec_buffer[tail].value = value;
  859         client->ec_buffer_tail = (tail + 1) % count;
  860 
  861         /* Allow users to read events only after report has been completed */
  862         if (type == EV_SYN && code == SYN_REPORT) {
  863                 evdev_client_gettime(client, &time);
  864                 for (; ready != client->ec_buffer_tail;
  865                     ready = (ready + 1) % count)
  866                         client->ec_buffer[ready].time = time;
  867                 client->ec_buffer_ready = client->ec_buffer_tail;
  868         }
  869 }
  870 
  871 void
  872 evdev_client_dumpqueue(struct evdev_client *client)
  873 {
  874         struct input_event *event;
  875         size_t i, head, tail, ready, size;
  876 
  877         head = client->ec_buffer_head;
  878         tail = client->ec_buffer_tail;
  879         ready = client->ec_buffer_ready;
  880         size = client->ec_buffer_size;
  881 
  882         printf("evdev client: %p\n", client);
  883         printf("event queue: head=%zu ready=%zu tail=%zu size=%zu\n",
  884             head, ready, tail, size);
  885 
  886         printf("queue contents:\n");
  887 
  888         for (i = 0; i < size; i++) {
  889                 event = &client->ec_buffer[i];
  890                 printf("%zu: ", i);
  891 
  892                 if (i < head || i > tail)
  893                         printf("unused\n");
  894                 else
  895                         printf("type=%d code=%d value=%d ", event->type,
  896                             event->code, event->value);
  897 
  898                 if (i == head)
  899                         printf("<- head\n");
  900                 else if (i == tail)
  901                         printf("<- tail\n");
  902                 else if (i == ready)
  903                         printf("<- ready\n");
  904                 else
  905                         printf("\n");
  906         }
  907 }
  908 
  909 static void
  910 evdev_client_filter_queue(struct evdev_client *client, uint16_t type)
  911 {
  912         struct input_event *event;
  913         size_t head, tail, count, i;
  914         bool last_was_syn = false;
  915 
  916         EVDEV_CLIENT_LOCKQ(client);
  917 
  918         i = head = client->ec_buffer_head;
  919         tail = client->ec_buffer_tail;
  920         count = client->ec_buffer_size;
  921         client->ec_buffer_ready = client->ec_buffer_tail;
  922 
  923         while (i != client->ec_buffer_tail) {
  924                 event = &client->ec_buffer[i];
  925                 i = (i + 1) % count;
  926 
  927                 /* Skip event of given type */
  928                 if (event->type == type)
  929                         continue;
  930 
  931                 /* Remove empty SYN_REPORT events */
  932                 if (event->type == EV_SYN && event->code == SYN_REPORT) {
  933                         if (last_was_syn)
  934                                 continue;
  935                         else
  936                                 client->ec_buffer_ready = (tail + 1) % count;
  937                 }
  938 
  939                 /* Rewrite entry */
  940                 memcpy(&client->ec_buffer[tail], event,
  941                     sizeof(struct input_event));
  942 
  943                 last_was_syn = (event->type == EV_SYN &&
  944                     event->code == SYN_REPORT);
  945 
  946                 tail = (tail + 1) % count;
  947         }
  948 
  949         client->ec_buffer_head = i;
  950         client->ec_buffer_tail = tail;
  951 
  952         EVDEV_CLIENT_UNLOCKQ(client);
  953 }

Cache object: ef1b17d986af30cbcdbcd251f6d21e25


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