struct list_head node;
        int clk_type;
        bool revoked;
+       unsigned long *evmasks[EV_CNT];
        unsigned int bufsize;
        struct input_event buffer[];
 };
 
+static size_t evdev_get_mask_cnt(unsigned int type)
+{
+       static const size_t counts[EV_CNT] = {
+               /* EV_SYN==0 is EV_CNT, _not_ SYN_CNT, see EVIOCGBIT */
+               [EV_SYN]        = EV_CNT,
+               [EV_KEY]        = KEY_CNT,
+               [EV_REL]        = REL_CNT,
+               [EV_ABS]        = ABS_CNT,
+               [EV_MSC]        = MSC_CNT,
+               [EV_SW]         = SW_CNT,
+               [EV_LED]        = LED_CNT,
+               [EV_SND]        = SND_CNT,
+               [EV_FF]         = FF_CNT,
+       };
+
+       return (type < EV_CNT) ? counts[type] : 0;
+}
+
+/* requires the buffer lock to be held */
+static bool __evdev_is_filtered(struct evdev_client *client,
+                               unsigned int type,
+                               unsigned int code)
+{
+       unsigned long *mask;
+       size_t cnt;
+
+       /* EV_SYN and unknown codes are never filtered */
+       if (type == EV_SYN || type >= EV_CNT)
+               return false;
+
+       /* first test whether the type is filtered */
+       mask = client->evmasks[0];
+       if (mask && !test_bit(type, mask))
+               return true;
+
+       /* unknown values are never filtered */
+       cnt = evdev_get_mask_cnt(type);
+       if (!cnt || code >= cnt)
+               return false;
+
+       mask = client->evmasks[type];
+       return mask && !test_bit(code, mask);
+}
+
 /* flush queued events of type @type, caller must hold client->buffer_lock */
 static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 {
        spin_lock(&client->buffer_lock);
 
        for (v = vals; v != vals + count; v++) {
+               if (__evdev_is_filtered(client, v->type, v->code))
+                       continue;
+
+               if (v->type == EV_SYN && v->code == SYN_REPORT) {
+                       /* drop empty SYN_REPORT */
+                       if (client->packet_head == client->head)
+                               continue;
+
+                       wakeup = true;
+               }
+
                event.type = v->type;
                event.code = v->code;
                event.value = v->value;
                __pass_event(client, &event);
-               if (v->type == EV_SYN && v->code == SYN_REPORT)
-                       wakeup = true;
        }
 
        spin_unlock(&client->buffer_lock);
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
+       unsigned int i;
 
        mutex_lock(&evdev->mutex);
        evdev_ungrab(evdev, client);
 
        evdev_detach_client(evdev, client);
 
+       for (i = 0; i < EV_CNT; ++i)
+               kfree(client->evmasks[i]);
+
        kvfree(client);
 
        evdev_close_device(evdev);
 
        return len;
 }
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+                         unsigned int maxlen, const void __user *p, int compat)
+{
+       int len, i;
+
+       if (compat) {
+               if (maxlen % sizeof(compat_long_t))
+                       return -EINVAL;
+
+               len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
+               if (len > maxlen)
+                       len = maxlen;
+
+               for (i = 0; i < len / sizeof(compat_long_t); i++)
+                       if (copy_from_user((compat_long_t *) bits +
+                                               i + 1 - ((i % 2) << 1),
+                                          (compat_long_t __user *) p + i,
+                                          sizeof(compat_long_t)))
+                               return -EFAULT;
+               if (i % 2)
+                       *((compat_long_t *) bits + i - 1) = 0;
+
+       } else {
+               if (maxlen % sizeof(long))
+                       return -EINVAL;
+
+               len = BITS_TO_LONGS(maxbit) * sizeof(long);
+               if (len > maxlen)
+                       len = maxlen;
+
+               if (copy_from_user(bits, p, len))
+                       return -EFAULT;
+       }
+
+       return len;
+}
+
 #else
+
 static int bits_to_user(unsigned long *bits, unsigned int maxbit,
                        unsigned int maxlen, void __user *p, int compat)
 {
 
        return copy_to_user(p, bits, len) ? -EFAULT : len;
 }
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+                         unsigned int maxlen, const void __user *p, int compat)
+{
+       size_t chunk_size = compat ? sizeof(compat_long_t) : sizeof(long);
+       int len;
+
+       if (maxlen % chunk_size)
+               return -EINVAL;
+
+       len = compat ? BITS_TO_LONGS_COMPAT(maxbit) : BITS_TO_LONGS(maxbit);
+       len *= chunk_size;
+       if (len > maxlen)
+               len = maxlen;
+
+       return copy_from_user(bits, p, len) ? -EFAULT : len;
+}
+
 #endif /* __BIG_ENDIAN */
 
 #else
        return copy_to_user(p, bits, len) ? -EFAULT : len;
 }
 
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+                         unsigned int maxlen, const void __user *p, int compat)
+{
+       int len;
+
+       if (maxlen % sizeof(long))
+               return -EINVAL;
+
+       len = BITS_TO_LONGS(maxbit) * sizeof(long);
+       if (len > maxlen)
+               len = maxlen;
+
+       return copy_from_user(bits, p, len) ? -EFAULT : len;
+}
+
 #endif /* CONFIG_COMPAT */
 
 static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
        return 0;
 }
 
+/* must be called with evdev-mutex held */
+static int evdev_set_mask(struct evdev_client *client,
+                         unsigned int type,
+                         const void __user *codes,
+                         u32 codes_size,
+                         int compat)
+{
+       unsigned long flags, *mask, *oldmask;
+       size_t cnt;
+       int error;
+
+       /* we allow unknown types and 'codes_size > size' for forward-compat */
+       cnt = evdev_get_mask_cnt(type);
+       if (!cnt)
+               return 0;
+
+       mask = kcalloc(sizeof(unsigned long), BITS_TO_LONGS(cnt), GFP_KERNEL);
+       if (!mask)
+               return -ENOMEM;
+
+       error = bits_from_user(mask, cnt - 1, codes_size, codes, compat);
+       if (error < 0) {
+               kfree(mask);
+               return error;
+       }
+
+       spin_lock_irqsave(&client->buffer_lock, flags);
+       oldmask = client->evmasks[type];
+       client->evmasks[type] = mask;
+       spin_unlock_irqrestore(&client->buffer_lock, flags);
+
+       kfree(oldmask);
+
+       return 0;
+}
+
+/* must be called with evdev-mutex held */
+static int evdev_get_mask(struct evdev_client *client,
+                         unsigned int type,
+                         void __user *codes,
+                         u32 codes_size,
+                         int compat)
+{
+       unsigned long *mask;
+       size_t cnt, size, xfer_size;
+       int i;
+       int error;
+
+       /* we allow unknown types and 'codes_size > size' for forward-compat */
+       cnt = evdev_get_mask_cnt(type);
+       size = sizeof(unsigned long) * BITS_TO_LONGS(cnt);
+       xfer_size = min_t(size_t, codes_size, size);
+
+       if (cnt > 0) {
+               mask = client->evmasks[type];
+               if (mask) {
+                       error = bits_to_user(mask, cnt - 1,
+                                            xfer_size, codes, compat);
+                       if (error < 0)
+                               return error;
+               } else {
+                       /* fake mask with all bits set */
+                       for (i = 0; i < xfer_size; i++)
+                               if (put_user(0xffU, (u8 __user *)codes + i))
+                                       return -EFAULT;
+               }
+       }
+
+       if (xfer_size < codes_size)
+               if (clear_user(codes + xfer_size, codes_size - xfer_size))
+                       return -EFAULT;
+
+       return 0;
+}
+
 static long evdev_do_ioctl(struct file *file, unsigned int cmd,
                           void __user *p, int compat_mode)
 {
        struct evdev *evdev = client->evdev;
        struct input_dev *dev = evdev->handle.dev;
        struct input_absinfo abs;
+       struct input_mask mask;
        struct ff_effect effect;
        int __user *ip = (int __user *)p;
        unsigned int i, t, u, v;
                else
                        return evdev_revoke(evdev, client, file);
 
+       case EVIOCGMASK: {
+               void __user *codes_ptr;
+
+               if (copy_from_user(&mask, p, sizeof(mask)))
+                       return -EFAULT;
+
+               codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;
+               return evdev_get_mask(client,
+                                     mask.type, codes_ptr, mask.codes_size,
+                                     compat_mode);
+       }
+
+       case EVIOCSMASK: {
+               const void __user *codes_ptr;
+
+               if (copy_from_user(&mask, p, sizeof(mask)))
+                       return -EFAULT;
+
+               codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;
+               return evdev_set_mask(client,
+                                     mask.type, codes_ptr, mask.codes_size,
+                                     compat_mode);
+       }
+
        case EVIOCSCLOCKID:
                if (copy_from_user(&i, p, sizeof(unsigned int)))
                        return -EFAULT;
 
        __u8  scancode[32];
 };
 
+struct input_mask {
+       __u32 type;
+       __u32 codes_size;
+       __u64 codes_ptr;
+};
+
 #define EVIOCGVERSION          _IOR('E', 0x01, int)                    /* get driver version */
 #define EVIOCGID               _IOR('E', 0x02, struct input_id)        /* get device ID */
 #define EVIOCGREP              _IOR('E', 0x03, unsigned int[2])        /* get repeat settings */
 #define EVIOCGRAB              _IOW('E', 0x90, int)                    /* Grab/Release device */
 #define EVIOCREVOKE            _IOW('E', 0x91, int)                    /* Revoke device access */
 
+/**
+ * EVIOCGMASK - Retrieve current event mask
+ *
+ * This ioctl allows user to retrieve the current event mask for specific
+ * event type. The argument must be of type "struct input_mask" and
+ * specifies the event type to query, the address of the receive buffer and
+ * the size of the receive buffer.
+ *
+ * The event mask is a per-client mask that specifies which events are
+ * forwarded to the client. Each event code is represented by a single bit
+ * in the event mask. If the bit is set, the event is passed to the client
+ * normally. Otherwise, the event is filtered and will never be queued on
+ * the client's receive buffer.
+ *
+ * Event masks do not affect global state of the input device. They only
+ * affect the file descriptor they are applied to.
+ *
+ * The default event mask for a client has all bits set, i.e. all events
+ * are forwarded to the client. If the kernel is queried for an unknown
+ * event type or if the receive buffer is larger than the number of
+ * event codes known to the kernel, the kernel returns all zeroes for those
+ * codes.
+ *
+ * At maximum, codes_size bytes are copied.
+ *
+ * This ioctl may fail with ENODEV in case the file is revoked, EFAULT
+ * if the receive-buffer points to invalid memory, or EINVAL if the kernel
+ * does not implement the ioctl.
+ */
+#define EVIOCGMASK             _IOR('E', 0x92, struct input_mask)      /* Get event-masks */
+
+/**
+ * EVIOCSMASK - Set event mask
+ *
+ * This ioctl is the counterpart to EVIOCGMASK. Instead of receiving the
+ * current event mask, this changes the client's event mask for a specific
+ * type.  See EVIOCGMASK for a description of event-masks and the
+ * argument-type.
+ *
+ * This ioctl provides full forward compatibility. If the passed event type
+ * is unknown to the kernel, or if the number of event codes specified in
+ * the mask is bigger than what is known to the kernel, the ioctl is still
+ * accepted and applied. However, any unknown codes are left untouched and
+ * stay cleared. That means, the kernel always filters unknown codes
+ * regardless of what the client requests.  If the new mask doesn't cover
+ * all known event-codes, all remaining codes are automatically cleared and
+ * thus filtered.
+ *
+ * This ioctl may fail with ENODEV in case the file is revoked. EFAULT is
+ * returned if the receive-buffer points to invalid memory. EINVAL is returned
+ * if the kernel does not implement the ioctl.
+ */
+#define EVIOCSMASK             _IOW('E', 0x93, struct input_mask)      /* Set event-masks */
+
 #define EVIOCSCLOCKID          _IOW('E', 0xa0, int)                    /* Set clockid to be used for timestamps */
 
 /*