|
@@ -969,6 +969,130 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * struct rc_filter_attribute - Device attribute relating to a filter type.
|
|
|
+ * @attr: Device attribute.
|
|
|
+ * @type: Filter type.
|
|
|
+ * @mask: false for filter value, true for filter mask.
|
|
|
+ */
|
|
|
+struct rc_filter_attribute {
|
|
|
+ struct device_attribute attr;
|
|
|
+ enum rc_filter_type type;
|
|
|
+ bool mask;
|
|
|
+};
|
|
|
+#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
|
|
|
+
|
|
|
+#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask) \
|
|
|
+ struct rc_filter_attribute dev_attr_##_name = { \
|
|
|
+ .attr = __ATTR(_name, _mode, _show, _store), \
|
|
|
+ .type = (_type), \
|
|
|
+ .mask = (_mask), \
|
|
|
+ }
|
|
|
+
|
|
|
+/**
|
|
|
+ * show_filter() - shows the current scancode filter value or mask
|
|
|
+ * @device: the device descriptor
|
|
|
+ * @attr: the device attribute struct
|
|
|
+ * @buf: a pointer to the output buffer
|
|
|
+ *
|
|
|
+ * This routine is a callback routine to read a scancode filter value or mask.
|
|
|
+ * It is trigged by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
|
|
|
+ * It prints the current scancode filter value or mask of the appropriate filter
|
|
|
+ * type in hexadecimal into @buf and returns the size of the buffer.
|
|
|
+ *
|
|
|
+ * Bits of the filter value corresponding to set bits in the filter mask are
|
|
|
+ * compared against input scancodes and non-matching scancodes are discarded.
|
|
|
+ *
|
|
|
+ * dev->lock is taken to guard against races between device registration,
|
|
|
+ * store_filter and show_filter.
|
|
|
+ */
|
|
|
+static ssize_t show_filter(struct device *device,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ struct rc_dev *dev = to_rc_dev(device);
|
|
|
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ /* Device is being removed */
|
|
|
+ if (!dev)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mutex_lock(&dev->lock);
|
|
|
+ if (!dev->s_filter)
|
|
|
+ val = 0;
|
|
|
+ else if (fattr->mask)
|
|
|
+ val = dev->scancode_filters[fattr->type].mask;
|
|
|
+ else
|
|
|
+ val = dev->scancode_filters[fattr->type].data;
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+
|
|
|
+ return sprintf(buf, "%#x\n", val);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * store_filter() - changes the scancode filter value
|
|
|
+ * @device: the device descriptor
|
|
|
+ * @attr: the device attribute struct
|
|
|
+ * @buf: a pointer to the input buffer
|
|
|
+ * @len: length of the input buffer
|
|
|
+ *
|
|
|
+ * This routine is for changing a scancode filter value or mask.
|
|
|
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
|
|
|
+ * Returns -EINVAL if an invalid filter value for the current protocol was
|
|
|
+ * specified or if scancode filtering is not supported by the driver, otherwise
|
|
|
+ * returns @len.
|
|
|
+ *
|
|
|
+ * Bits of the filter value corresponding to set bits in the filter mask are
|
|
|
+ * compared against input scancodes and non-matching scancodes are discarded.
|
|
|
+ *
|
|
|
+ * dev->lock is taken to guard against races between device registration,
|
|
|
+ * store_filter and show_filter.
|
|
|
+ */
|
|
|
+static ssize_t store_filter(struct device *device,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf,
|
|
|
+ size_t count)
|
|
|
+{
|
|
|
+ struct rc_dev *dev = to_rc_dev(device);
|
|
|
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
|
|
|
+ struct rc_scancode_filter local_filter, *filter;
|
|
|
+ int ret;
|
|
|
+ unsigned long val;
|
|
|
+
|
|
|
+ /* Device is being removed */
|
|
|
+ if (!dev)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ ret = kstrtoul(buf, 0, &val);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* Scancode filter not supported (but still accept 0) */
|
|
|
+ if (!dev->s_filter)
|
|
|
+ return val ? -EINVAL : count;
|
|
|
+
|
|
|
+ mutex_lock(&dev->lock);
|
|
|
+
|
|
|
+ /* Tell the driver about the new filter */
|
|
|
+ filter = &dev->scancode_filters[fattr->type];
|
|
|
+ local_filter = *filter;
|
|
|
+ if (fattr->mask)
|
|
|
+ local_filter.mask = val;
|
|
|
+ else
|
|
|
+ local_filter.data = val;
|
|
|
+ ret = dev->s_filter(dev, fattr->type, &local_filter);
|
|
|
+ if (ret < 0)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ /* Success, commit the new filter */
|
|
|
+ *filter = local_filter;
|
|
|
+
|
|
|
+unlock:
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
static void rc_dev_release(struct device *device)
|
|
|
{
|
|
|
}
|
|
@@ -1000,9 +1124,21 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
|
|
|
*/
|
|
|
static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
|
|
|
show_protocols, store_protocols);
|
|
|
+static RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR,
|
|
|
+ show_filter, store_filter, RC_FILTER_NORMAL, false);
|
|
|
+static RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR,
|
|
|
+ show_filter, store_filter, RC_FILTER_NORMAL, true);
|
|
|
+static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR,
|
|
|
+ show_filter, store_filter, RC_FILTER_WAKEUP, false);
|
|
|
+static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
|
|
|
+ show_filter, store_filter, RC_FILTER_WAKEUP, true);
|
|
|
|
|
|
static struct attribute *rc_dev_attrs[] = {
|
|
|
&dev_attr_protocols.attr,
|
|
|
+ &dev_attr_filter.attr.attr,
|
|
|
+ &dev_attr_filter_mask.attr.attr,
|
|
|
+ &dev_attr_wakeup_filter.attr.attr,
|
|
|
+ &dev_attr_wakeup_filter_mask.attr.attr,
|
|
|
NULL,
|
|
|
};
|
|
|
|